@selvajs/server 1.1.0 → 1.3.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 (233) hide show
  1. package/dist/access/guards.d.ts +213 -0
  2. package/dist/access/guards.d.ts.map +1 -0
  3. package/dist/access/guards.js +417 -0
  4. package/dist/access/guards.js.map +1 -0
  5. package/dist/access/index.d.ts +2 -0
  6. package/dist/access/index.d.ts.map +1 -1
  7. package/dist/access/index.js +6 -0
  8. package/dist/access/index.js.map +1 -1
  9. package/dist/access/permissions-scope.d.ts +15 -0
  10. package/dist/access/permissions-scope.d.ts.map +1 -0
  11. package/dist/access/permissions-scope.js +23 -0
  12. package/dist/access/permissions-scope.js.map +1 -0
  13. package/dist/api/bodies.d.ts +104 -0
  14. package/dist/api/bodies.d.ts.map +1 -0
  15. package/dist/api/bodies.js +87 -0
  16. package/dist/api/bodies.js.map +1 -0
  17. package/dist/api/deps.d.ts +135 -0
  18. package/dist/api/deps.d.ts.map +1 -0
  19. package/dist/api/deps.js +55 -0
  20. package/dist/api/deps.js.map +1 -0
  21. package/dist/api/errors.d.ts +35 -0
  22. package/dist/api/errors.d.ts.map +1 -0
  23. package/dist/api/errors.js +63 -0
  24. package/dist/api/errors.js.map +1 -0
  25. package/dist/api/index.d.ts +12 -0
  26. package/dist/api/index.d.ts.map +1 -0
  27. package/dist/api/index.js +15 -0
  28. package/dist/api/index.js.map +1 -0
  29. package/dist/api/map-core-error.d.ts +19 -0
  30. package/dist/api/map-core-error.d.ts.map +1 -0
  31. package/dist/api/map-core-error.js +49 -0
  32. package/dist/api/map-core-error.js.map +1 -0
  33. package/dist/api/pagination.d.ts +19 -0
  34. package/dist/api/pagination.d.ts.map +1 -0
  35. package/dist/api/pagination.js +40 -0
  36. package/dist/api/pagination.js.map +1 -0
  37. package/dist/api/request.d.ts +93 -0
  38. package/dist/api/request.d.ts.map +1 -0
  39. package/dist/api/request.js +150 -0
  40. package/dist/api/request.js.map +1 -0
  41. package/dist/api/respond.d.ts +29 -0
  42. package/dist/api/respond.d.ts.map +1 -0
  43. package/dist/api/respond.js +59 -0
  44. package/dist/api/respond.js.map +1 -0
  45. package/dist/api/responses-schema.d.ts +158 -0
  46. package/dist/api/responses-schema.d.ts.map +1 -0
  47. package/dist/api/responses-schema.js +104 -0
  48. package/dist/api/responses-schema.js.map +1 -0
  49. package/dist/api/responses.d.ts +19 -0
  50. package/dist/api/responses.d.ts.map +1 -0
  51. package/dist/api/responses.js +21 -0
  52. package/dist/api/responses.js.map +1 -0
  53. package/dist/api/shaped.d.ts +23 -0
  54. package/dist/api/shaped.d.ts.map +1 -0
  55. package/dist/api/shaped.js +29 -0
  56. package/dist/api/shaped.js.map +1 -0
  57. package/dist/api/types.d.ts +41 -0
  58. package/dist/api/types.d.ts.map +1 -0
  59. package/dist/api/types.js +12 -0
  60. package/dist/api/types.js.map +1 -0
  61. package/dist/compute/errors.d.ts +18 -0
  62. package/dist/compute/errors.d.ts.map +1 -0
  63. package/dist/compute/errors.js +21 -0
  64. package/dist/compute/errors.js.map +1 -0
  65. package/dist/compute/evict-changed-servers.d.ts +22 -0
  66. package/dist/compute/evict-changed-servers.d.ts.map +1 -0
  67. package/dist/compute/evict-changed-servers.js +31 -0
  68. package/dist/compute/evict-changed-servers.js.map +1 -0
  69. package/dist/compute/idempotency-http.d.ts +47 -0
  70. package/dist/compute/idempotency-http.d.ts.map +1 -0
  71. package/dist/compute/idempotency-http.js +51 -0
  72. package/dist/compute/idempotency-http.js.map +1 -0
  73. package/dist/compute/index.d.ts +5 -0
  74. package/dist/compute/index.d.ts.map +1 -1
  75. package/dist/compute/index.js +5 -0
  76. package/dist/compute/index.js.map +1 -1
  77. package/dist/compute/resolve-server.d.ts +25 -0
  78. package/dist/compute/resolve-server.d.ts.map +1 -0
  79. package/dist/compute/resolve-server.js +35 -0
  80. package/dist/compute/resolve-server.js.map +1 -0
  81. package/dist/compute/server-config-write.d.ts +52 -0
  82. package/dist/compute/server-config-write.d.ts.map +1 -0
  83. package/dist/compute/server-config-write.js +100 -0
  84. package/dist/compute/server-config-write.js.map +1 -0
  85. package/dist/definitions/index.d.ts +1 -0
  86. package/dist/definitions/index.d.ts.map +1 -1
  87. package/dist/definitions/index.js +1 -0
  88. package/dist/definitions/index.js.map +1 -1
  89. package/dist/definitions/visibility.d.ts +74 -0
  90. package/dist/definitions/visibility.d.ts.map +1 -0
  91. package/dist/definitions/visibility.js +117 -0
  92. package/dist/definitions/visibility.js.map +1 -0
  93. package/dist/handlers/definitionVersions.d.ts +30 -0
  94. package/dist/handlers/definitionVersions.d.ts.map +1 -0
  95. package/dist/handlers/definitionVersions.js +93 -0
  96. package/dist/handlers/definitionVersions.js.map +1 -0
  97. package/dist/handlers/definitions.d.ts +35 -0
  98. package/dist/handlers/definitions.d.ts.map +1 -0
  99. package/dist/handlers/definitions.js +207 -0
  100. package/dist/handlers/definitions.js.map +1 -0
  101. package/dist/handlers/index.d.ts +27 -0
  102. package/dist/handlers/index.d.ts.map +1 -0
  103. package/dist/handlers/index.js +29 -0
  104. package/dist/handlers/index.js.map +1 -0
  105. package/dist/handlers/invite-lookup.d.ts +11 -0
  106. package/dist/handlers/invite-lookup.d.ts.map +1 -0
  107. package/dist/handlers/invite-lookup.js +20 -0
  108. package/dist/handlers/invite-lookup.js.map +1 -0
  109. package/dist/handlers/invites.d.ts +31 -0
  110. package/dist/handlers/invites.d.ts.map +1 -0
  111. package/dist/handlers/invites.js +148 -0
  112. package/dist/handlers/invites.js.map +1 -0
  113. package/dist/handlers/me.d.ts +8 -0
  114. package/dist/handlers/me.d.ts.map +1 -0
  115. package/dist/handlers/me.js +21 -0
  116. package/dist/handlers/me.js.map +1 -0
  117. package/dist/handlers/me.starred.d.ts +11 -0
  118. package/dist/handlers/me.starred.d.ts.map +1 -0
  119. package/dist/handlers/me.starred.js +21 -0
  120. package/dist/handlers/me.starred.js.map +1 -0
  121. package/dist/handlers/orgAssets.d.ts +13 -0
  122. package/dist/handlers/orgAssets.d.ts.map +1 -0
  123. package/dist/handlers/orgAssets.js +54 -0
  124. package/dist/handlers/orgAssets.js.map +1 -0
  125. package/dist/handlers/orgCompute.d.ts +16 -0
  126. package/dist/handlers/orgCompute.d.ts.map +1 -0
  127. package/dist/handlers/orgCompute.js +96 -0
  128. package/dist/handlers/orgCompute.js.map +1 -0
  129. package/dist/handlers/orgMembers.d.ts +29 -0
  130. package/dist/handlers/orgMembers.d.ts.map +1 -0
  131. package/dist/handlers/orgMembers.js +200 -0
  132. package/dist/handlers/orgMembers.js.map +1 -0
  133. package/dist/handlers/orgs.d.ts +16 -0
  134. package/dist/handlers/orgs.d.ts.map +1 -0
  135. package/dist/handlers/orgs.js +26 -0
  136. package/dist/handlers/orgs.js.map +1 -0
  137. package/dist/handlers/projectMembers.d.ts +15 -0
  138. package/dist/handlers/projectMembers.d.ts.map +1 -0
  139. package/dist/handlers/projectMembers.js +95 -0
  140. package/dist/handlers/projectMembers.js.map +1 -0
  141. package/dist/handlers/projects.d.ts +21 -0
  142. package/dist/handlers/projects.d.ts.map +1 -0
  143. package/dist/handlers/projects.js +164 -0
  144. package/dist/handlers/projects.js.map +1 -0
  145. package/dist/handlers/reclaim.d.ts +10 -0
  146. package/dist/handlers/reclaim.d.ts.map +1 -0
  147. package/dist/handlers/reclaim.js +38 -0
  148. package/dist/handlers/reclaim.js.map +1 -0
  149. package/dist/handlers/services.d.ts +22 -0
  150. package/dist/handlers/services.d.ts.map +1 -0
  151. package/dist/handlers/services.js +37 -0
  152. package/dist/handlers/services.js.map +1 -0
  153. package/dist/handlers/shareLinks.d.ts +23 -0
  154. package/dist/handlers/shareLinks.d.ts.map +1 -0
  155. package/dist/handlers/shareLinks.js +85 -0
  156. package/dist/handlers/shareLinks.js.map +1 -0
  157. package/dist/http/index.d.ts +1 -0
  158. package/dist/http/index.d.ts.map +1 -1
  159. package/dist/http/index.js +1 -0
  160. package/dist/http/index.js.map +1 -1
  161. package/dist/http/login-rate-limit.d.ts +72 -0
  162. package/dist/http/login-rate-limit.d.ts.map +1 -0
  163. package/dist/http/login-rate-limit.js +84 -0
  164. package/dist/http/login-rate-limit.js.map +1 -0
  165. package/dist/notifications/deliver-invite.d.ts +27 -0
  166. package/dist/notifications/deliver-invite.d.ts.map +1 -0
  167. package/dist/notifications/deliver-invite.js +32 -0
  168. package/dist/notifications/deliver-invite.js.map +1 -0
  169. package/dist/notifications/index.d.ts +4 -0
  170. package/dist/notifications/index.d.ts.map +1 -0
  171. package/dist/notifications/index.js +4 -0
  172. package/dist/notifications/index.js.map +1 -0
  173. package/dist/notifications/smtp-config.d.ts +37 -0
  174. package/dist/notifications/smtp-config.d.ts.map +1 -0
  175. package/dist/notifications/smtp-config.js +41 -0
  176. package/dist/notifications/smtp-config.js.map +1 -0
  177. package/dist/notifications/smtp-provider.d.ts +18 -0
  178. package/dist/notifications/smtp-provider.d.ts.map +1 -0
  179. package/dist/notifications/smtp-provider.js +71 -0
  180. package/dist/notifications/smtp-provider.js.map +1 -0
  181. package/dist/ops/deploymentConfig.d.ts +73 -0
  182. package/dist/ops/deploymentConfig.d.ts.map +1 -0
  183. package/dist/ops/deploymentConfig.js +154 -0
  184. package/dist/ops/deploymentConfig.js.map +1 -0
  185. package/dist/ops/index.d.ts +1 -0
  186. package/dist/ops/index.d.ts.map +1 -1
  187. package/dist/ops/index.js +3 -2
  188. package/dist/ops/index.js.map +1 -1
  189. package/dist/organizations/index.d.ts +3 -0
  190. package/dist/organizations/index.d.ts.map +1 -0
  191. package/dist/organizations/index.js +5 -0
  192. package/dist/organizations/index.js.map +1 -0
  193. package/dist/organizations/list-all-members.d.ts +10 -0
  194. package/dist/organizations/list-all-members.d.ts.map +1 -0
  195. package/dist/organizations/list-all-members.js +36 -0
  196. package/dist/organizations/list-all-members.js.map +1 -0
  197. package/dist/organizations/org-asset-service.d.ts +39 -0
  198. package/dist/organizations/org-asset-service.d.ts.map +1 -0
  199. package/dist/organizations/org-asset-service.js +71 -0
  200. package/dist/organizations/org-asset-service.js.map +1 -0
  201. package/dist/projects/create-project.d.ts +30 -0
  202. package/dist/projects/create-project.d.ts.map +1 -0
  203. package/dist/projects/create-project.js +56 -0
  204. package/dist/projects/create-project.js.map +1 -0
  205. package/dist/projects/index.d.ts +2 -0
  206. package/dist/projects/index.d.ts.map +1 -0
  207. package/dist/projects/index.js +3 -0
  208. package/dist/projects/index.js.map +1 -0
  209. package/dist/providers/auth-lookup.d.ts +14 -0
  210. package/dist/providers/auth-lookup.d.ts.map +1 -0
  211. package/dist/providers/auth-lookup.js +34 -0
  212. package/dist/providers/auth-lookup.js.map +1 -0
  213. package/dist/providers/index.d.ts +1 -0
  214. package/dist/providers/index.d.ts.map +1 -1
  215. package/dist/providers/index.js +1 -0
  216. package/dist/providers/index.js.map +1 -1
  217. package/dist/testing/harness.d.ts +168 -0
  218. package/dist/testing/harness.d.ts.map +1 -0
  219. package/dist/testing/harness.js +314 -0
  220. package/dist/testing/harness.js.map +1 -0
  221. package/dist/testing/index.d.ts +18 -0
  222. package/dist/testing/index.d.ts.map +1 -0
  223. package/dist/testing/index.js +15 -0
  224. package/dist/testing/index.js.map +1 -0
  225. package/dist/testing/scenarios.d.ts +67 -0
  226. package/dist/testing/scenarios.d.ts.map +1 -0
  227. package/dist/testing/scenarios.js +104 -0
  228. package/dist/testing/scenarios.js.map +1 -0
  229. package/dist/testing/seed-adapter.d.ts +31 -0
  230. package/dist/testing/seed-adapter.d.ts.map +1 -0
  231. package/dist/testing/seed-adapter.js +19 -0
  232. package/dist/testing/seed-adapter.js.map +1 -0
  233. package/package.json +42 -3
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The domain errors this package's own handlers raise, folded into the envelope.
3
+ *
4
+ * A host's `mapError` should call this first and add only its own error types.
5
+ * These rules cannot live solely in a host: the handler that throws them ships
6
+ * from here, so a host that forgot one would serve 500 where every other host
7
+ * serves 503 — the same API answering differently depending on who mounted it.
8
+ *
9
+ * Postgres surfaces a unique-constraint name verbatim, which is useless to an
10
+ * end user, so the constraints this package's handlers can trip are translated
11
+ * here rather than left to each host.
12
+ */
13
+ import { ApiError } from './errors.js';
14
+ /**
15
+ * Compute unreachable or serving an unreadable schema is operator-side (503); a
16
+ * schema the parser cannot read is the caller's file (422).
17
+ */
18
+ export declare function mapCoreError(err: unknown): ApiError | undefined;
19
+ //# sourceMappingURL=map-core-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-core-error.d.ts","sourceRoot":"","sources":["../../src/api/map-core-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAA+B,MAAM,aAAa,CAAC;AAmBpE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,CAc/D"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The domain errors this package's own handlers raise, folded into the envelope.
3
+ *
4
+ * A host's `mapError` should call this first and add only its own error types.
5
+ * These rules cannot live solely in a host: the handler that throws them ships
6
+ * from here, so a host that forgot one would serve 500 where every other host
7
+ * serves 503 — the same API answering differently depending on who mounted it.
8
+ *
9
+ * Postgres surfaces a unique-constraint name verbatim, which is useless to an
10
+ * end user, so the constraints this package's handlers can trip are translated
11
+ * here rather than left to each host.
12
+ */
13
+ import { ApiError, ApiErrorCode, codeForStatus } from './errors.js';
14
+ import { ComputeServerUnconfiguredError } from '../compute/errors.js';
15
+ import { SchemaExtractionError } from '../definitions/schema-extraction.js';
16
+ import { ProviderError } from '@selvajs/platform';
17
+ const UNIQUE_CONSTRAINT_MESSAGES = {
18
+ projects_org_name_unique: 'A project with that name already exists in this organization.',
19
+ projects_org_id_slug_key: 'A project with that name already exists in this organization.',
20
+ orgs_slug_key: 'An organization with that slug already exists.',
21
+ definitions_pkey: 'A definition with that ID already exists.'
22
+ };
23
+ function friendlyConstraintMessage(raw) {
24
+ for (const [name, msg] of Object.entries(UNIQUE_CONSTRAINT_MESSAGES)) {
25
+ if (raw.includes(name))
26
+ return msg;
27
+ }
28
+ return null;
29
+ }
30
+ /**
31
+ * Compute unreachable or serving an unreadable schema is operator-side (503); a
32
+ * schema the parser cannot read is the caller's file (422).
33
+ */
34
+ export function mapCoreError(err) {
35
+ if (err instanceof SchemaExtractionError) {
36
+ return err.kind === 'unreachable' || err.kind === 'malformed'
37
+ ? new ApiError(503, ApiErrorCode.COMPUTE_UNAVAILABLE, err.message)
38
+ : new ApiError(422, ApiErrorCode.UNPROCESSABLE, err.message);
39
+ }
40
+ if (err instanceof ComputeServerUnconfiguredError) {
41
+ return new ApiError(503, ApiErrorCode.COMPUTE_UNAVAILABLE, err.message);
42
+ }
43
+ if (err instanceof ProviderError) {
44
+ const friendly = friendlyConstraintMessage(err.message);
45
+ return new ApiError(err.statusCode, codeForStatus(err.statusCode), friendly ?? err.message);
46
+ }
47
+ return undefined;
48
+ }
49
+ //# sourceMappingURL=map-core-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-core-error.js","sourceRoot":"","sources":["../../src/api/map-core-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,8BAA8B,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,0BAA0B,GAA2B;IAC1D,wBAAwB,EAAE,+DAA+D;IACzF,wBAAwB,EAAE,+DAA+D;IACzF,aAAa,EAAE,gDAAgD;IAC/D,gBAAgB,EAAE,2CAA2C;CAC7D,CAAC;AAEF,SAAS,yBAAyB,CAAC,GAAW;IAC7C,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,0BAA0B,CAAC,EAAE,CAAC;QACtE,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,GAAG,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY;IACxC,IAAI,GAAG,YAAY,qBAAqB,EAAE,CAAC;QAC1C,OAAO,GAAG,CAAC,IAAI,KAAK,aAAa,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW;YAC5D,CAAC,CAAC,IAAI,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,mBAAmB,EAAE,GAAG,CAAC,OAAO,CAAC;YAClE,CAAC,CAAC,IAAI,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,aAAa,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,GAAG,YAAY,8BAA8B,EAAE,CAAC;QACnD,OAAO,IAAI,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,mBAAmB,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;QAClC,MAAM,QAAQ,GAAG,yBAAyB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACxD,OAAO,IAAI,QAAQ,CAAC,GAAG,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,QAAQ,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Query-param parsing for the list pagination contract:
3
+ * `?limit=&cursor=&orderBy=&orderDir=` → `{ items, nextCursor? }`.
4
+ *
5
+ * Every list endpoint parses these identically, so the clamping lives here
6
+ * rather than inline per handler — a handler that clamps differently publishes
7
+ * a different contract under the same documented one, and nothing fails at
8
+ * build time.
9
+ */
10
+ import type { DefinitionListOptions, ListOptions } from '@selvajs/platform';
11
+ /**
12
+ * An unparseable or out-of-range `limit` clamps rather than 400s: pagination
13
+ * params are plumbing, and failing a read over one is hostile to clients
14
+ * walking a cursor.
15
+ */
16
+ export declare function parseListOptions(url: URL): ListOptions;
17
+ /** `parseListOptions` plus the definition-only `solveCount` ordering. */
18
+ export declare function parseDefinitionListOptions(url: URL): DefinitionListOptions;
19
+ //# sourceMappingURL=pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/api/pagination.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE5E;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,WAAW,CAkBtD;AAED,yEAAyE;AACzE,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,GAAG,GAAG,qBAAqB,CAM1E"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Query-param parsing for the list pagination contract:
3
+ * `?limit=&cursor=&orderBy=&orderDir=` → `{ items, nextCursor? }`.
4
+ *
5
+ * Every list endpoint parses these identically, so the clamping lives here
6
+ * rather than inline per handler — a handler that clamps differently publishes
7
+ * a different contract under the same documented one, and nothing fails at
8
+ * build time.
9
+ */
10
+ import { DEFAULT_PAGE_LIMIT, MAX_PAGE_LIMIT } from '@selvajs/platform';
11
+ /**
12
+ * An unparseable or out-of-range `limit` clamps rather than 400s: pagination
13
+ * params are plumbing, and failing a read over one is hostile to clients
14
+ * walking a cursor.
15
+ */
16
+ export function parseListOptions(url) {
17
+ const raw = Number(url.searchParams.get('limit') ?? DEFAULT_PAGE_LIMIT);
18
+ const limit = Number.isFinite(raw)
19
+ ? Math.min(Math.max(Math.trunc(raw), 1), MAX_PAGE_LIMIT)
20
+ : DEFAULT_PAGE_LIMIT;
21
+ const orderByRaw = url.searchParams.get('orderBy');
22
+ const orderBy = orderByRaw === 'createdAt' || orderByRaw === 'updatedAt' || orderByRaw === 'name'
23
+ ? orderByRaw
24
+ : undefined;
25
+ return {
26
+ limit,
27
+ cursor: url.searchParams.get('cursor') ?? undefined,
28
+ orderBy,
29
+ orderDir: url.searchParams.get('orderDir') === 'asc' ? 'asc' : undefined
30
+ };
31
+ }
32
+ /** `parseListOptions` plus the definition-only `solveCount` ordering. */
33
+ export function parseDefinitionListOptions(url) {
34
+ const base = parseListOptions(url);
35
+ if (url.searchParams.get('orderBy') === 'solveCount') {
36
+ return { ...base, orderBy: 'solveCount' };
37
+ }
38
+ return base;
39
+ }
40
+ //# sourceMappingURL=pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/api/pagination.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGvE;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAQ;IACxC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,CAAC;IACxE,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QACjC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,cAAc,CAAC;QACxD,CAAC,CAAC,kBAAkB,CAAC;IAEtB,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACnD,MAAM,OAAO,GACZ,UAAU,KAAK,WAAW,IAAI,UAAU,KAAK,WAAW,IAAI,UAAU,KAAK,MAAM;QAChF,CAAC,CAAC,UAAU;QACZ,CAAC,CAAC,SAAS,CAAC;IAEd,OAAO;QACN,KAAK;QACL,MAAM,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS;QACnD,OAAO;QACP,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;KACxE,CAAC;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,0BAA0B,CAAC,GAAQ;IAClD,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,YAAY,EAAE,CAAC;QACtD,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Reading a request: path params, JSON bodies, multipart uploads.
3
+ *
4
+ * Every helper here fails by throwing `ApiError`, so a handler using them names
5
+ * no web framework. They were written against SvelteKit's `error()` and moved
6
+ * once the handlers themselves became transport-free — the logic never depended
7
+ * on the framework, only the throw did.
8
+ *
9
+ * `requireCaller` reads identity off `ApiRequest`, never off a host's `locals`
10
+ * — a host maps its own session shape onto `ctx`/`user` when it builds the
11
+ * request, and nothing below this line knows how it did that.
12
+ */
13
+ import type { AuthUser, RequestContext } from '@selvajs/platform';
14
+ import type { ZodError, ZodType } from 'zod';
15
+ import type { ApiRequest } from './types.js';
16
+ /**
17
+ * The authenticated caller, or a 401.
18
+ *
19
+ * `ctx` and `user` are optional on `ApiRequest` because a host builds one for
20
+ * anonymous requests too (share-token solves reach handlers with a `ctx` and no
21
+ * `user`). Every handler that needs an identity narrows it here rather than
22
+ * asserting the fields non-null, so the unauthenticated path fails as a denial
23
+ * instead of a crash.
24
+ */
25
+ export declare function requireCaller(req: ApiRequest): {
26
+ ctx: RequestContext;
27
+ user: AuthUser;
28
+ };
29
+ /**
30
+ * Fail with a Zod error's messages, one per field.
31
+ *
32
+ * The top-level `message` names the first problem so a human reading the
33
+ * response learns something without parsing `fields`.
34
+ */
35
+ export declare function throwZodError(err: ZodError): never;
36
+ /**
37
+ * Read required path params, 400ing on any that is missing or blank.
38
+ *
39
+ * A router types every param as `string`, but a route can be reached with an
40
+ * empty segment — this catches that instead of letting a stale non-null
41
+ * assertion paper over it.
42
+ *
43
+ * Does **not** validate format — a guid-shaped param still goes through a
44
+ * schema where the route cares. Replaces the `if (!id) apiError(...)` preamble,
45
+ * not the schema check.
46
+ */
47
+ export declare function requireParams<K extends string>(params: Partial<Record<string, string>>, ...names: K[]): Record<K, string>;
48
+ /**
49
+ * Read a path param through a Zod schema, 400ing on a bad value.
50
+ *
51
+ * The format-checking counterpart to `requireParams` — for guids, enum-shaped
52
+ * segments, anything where "present" isn't the same as "valid".
53
+ */
54
+ export declare function parseParam<T>(value: string | undefined, schema: ZodType<T>, name: string): T;
55
+ /**
56
+ * Parse and validate a JSON body. A malformed body becomes `null` rather than
57
+ * throwing, so it fails the schema as a validation error — broken JSON gets a
58
+ * 400 naming the problem, not a 500 from `request.json()`.
59
+ *
60
+ * `missingAs` covers endpoints whose fields are all optional: passing `{}` lets
61
+ * an empty body mean "no changes" instead of a validation failure.
62
+ */
63
+ export declare function parseBody<T>(request: Request, schema: ZodType<T>, { missingAs }?: {
64
+ missingAs?: unknown;
65
+ }): Promise<T>;
66
+ /**
67
+ * Pull a required file out of a multipart body and check its size and, where
68
+ * given, its extension.
69
+ *
70
+ * A second line of defence, not the first: a host's body-size guard should
71
+ * reject an oversized request before it is buffered. This catches a file that
72
+ * is individually too large inside a request that isn't.
73
+ *
74
+ * Caps are passed in rather than read here — they are deployment config, and a
75
+ * package that resolved them would pin every host to one source of truth.
76
+ */
77
+ export declare function requireUpload(form: FormData, field: string, { maxBytes, extensions, contentTypes, label }: {
78
+ maxBytes: number;
79
+ extensions?: readonly string[];
80
+ contentTypes?: {
81
+ allowed: ReadonlySet<string>;
82
+ message: string;
83
+ };
84
+ label?: string;
85
+ }): {
86
+ file: File;
87
+ extension: string;
88
+ };
89
+ /** A multipart text field, trimmed and length-capped. Absent stays `undefined`. */
90
+ export declare function formText(form: FormData, field: string, { maxLength }?: {
91
+ maxLength?: number;
92
+ }): string | undefined;
93
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/api/request.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAE7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG;IAAE,GAAG,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,CAKtF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,KAAK,CAWlD;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC7C,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACvC,GAAG,KAAK,EAAE,CAAC,EAAE,GACX,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAqBnB;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAM5F;AAMD;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAChC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,EAClB,EAAE,SAAS,EAAE,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACzC,OAAO,CAAC,CAAC,CAAC,CAKZ;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC5B,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,EACb,EACC,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,KAAc,EACd,EAAE;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,YAAY,CAAC,EAAE;QAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;CACf,GACC;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CA4BnC;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CACvB,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,EACb,EAAE,SAAS,EAAE,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACxC,MAAM,GAAG,SAAS,CAMpB"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Reading a request: path params, JSON bodies, multipart uploads.
3
+ *
4
+ * Every helper here fails by throwing `ApiError`, so a handler using them names
5
+ * no web framework. They were written against SvelteKit's `error()` and moved
6
+ * once the handlers themselves became transport-free — the logic never depended
7
+ * on the framework, only the throw did.
8
+ *
9
+ * `requireCaller` reads identity off `ApiRequest`, never off a host's `locals`
10
+ * — a host maps its own session shape onto `ctx`/`user` when it builds the
11
+ * request, and nothing below this line knows how it did that.
12
+ */
13
+ import { apiError, ApiErrorCode } from './errors.js';
14
+ /**
15
+ * The authenticated caller, or a 401.
16
+ *
17
+ * `ctx` and `user` are optional on `ApiRequest` because a host builds one for
18
+ * anonymous requests too (share-token solves reach handlers with a `ctx` and no
19
+ * `user`). Every handler that needs an identity narrows it here rather than
20
+ * asserting the fields non-null, so the unauthenticated path fails as a denial
21
+ * instead of a crash.
22
+ */
23
+ export function requireCaller(req) {
24
+ if (!req.ctx || !req.user) {
25
+ apiError(401, ApiErrorCode.UNAUTHORIZED, 'Unauthorized');
26
+ }
27
+ return { ctx: req.ctx, user: req.user };
28
+ }
29
+ /**
30
+ * Fail with a Zod error's messages, one per field.
31
+ *
32
+ * The top-level `message` names the first problem so a human reading the
33
+ * response learns something without parsing `fields`.
34
+ */
35
+ export function throwZodError(err) {
36
+ const fields = {};
37
+ for (const issue of err.issues) {
38
+ const key = issue.path.length ? issue.path.join('.') : '_';
39
+ // First message per field wins; later issues on the same path are
40
+ // usually redundant refinements.
41
+ if (!(key in fields))
42
+ fields[key] = issue.message;
43
+ }
44
+ const first = err.issues[0];
45
+ const path = first.path.length ? `${first.path.join('.')}: ` : '';
46
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, `${path}${first.message}`, fields);
47
+ }
48
+ // ============================================================================
49
+ // Path params
50
+ // ============================================================================
51
+ /**
52
+ * Read required path params, 400ing on any that is missing or blank.
53
+ *
54
+ * A router types every param as `string`, but a route can be reached with an
55
+ * empty segment — this catches that instead of letting a stale non-null
56
+ * assertion paper over it.
57
+ *
58
+ * Does **not** validate format — a guid-shaped param still goes through a
59
+ * schema where the route cares. Replaces the `if (!id) apiError(...)` preamble,
60
+ * not the schema check.
61
+ */
62
+ export function requireParams(params, ...names) {
63
+ const out = {};
64
+ const missing = [];
65
+ for (const name of names) {
66
+ const value = params[name];
67
+ if (!value) {
68
+ missing.push(name);
69
+ continue;
70
+ }
71
+ out[name] = value;
72
+ }
73
+ if (missing.length) {
74
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, `Missing path parameter${missing.length > 1 ? 's' : ''}: ${missing.join(', ')}`);
75
+ }
76
+ return out;
77
+ }
78
+ /**
79
+ * Read a path param through a Zod schema, 400ing on a bad value.
80
+ *
81
+ * The format-checking counterpart to `requireParams` — for guids, enum-shaped
82
+ * segments, anything where "present" isn't the same as "valid".
83
+ */
84
+ export function parseParam(value, schema, name) {
85
+ const parsed = schema.safeParse(value);
86
+ if (!parsed.success) {
87
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, `Invalid or missing ${name}`);
88
+ }
89
+ return parsed.data;
90
+ }
91
+ // ============================================================================
92
+ // Request bodies
93
+ // ============================================================================
94
+ /**
95
+ * Parse and validate a JSON body. A malformed body becomes `null` rather than
96
+ * throwing, so it fails the schema as a validation error — broken JSON gets a
97
+ * 400 naming the problem, not a 500 from `request.json()`.
98
+ *
99
+ * `missingAs` covers endpoints whose fields are all optional: passing `{}` lets
100
+ * an empty body mean "no changes" instead of a validation failure.
101
+ */
102
+ export async function parseBody(request, schema, { missingAs } = {}) {
103
+ const raw = await request.json().catch(() => null);
104
+ const parsed = schema.safeParse(raw ?? missingAs ?? null);
105
+ if (!parsed.success)
106
+ throwZodError(parsed.error);
107
+ return parsed.data;
108
+ }
109
+ // ============================================================================
110
+ // Uploads
111
+ // ============================================================================
112
+ /**
113
+ * Pull a required file out of a multipart body and check its size and, where
114
+ * given, its extension.
115
+ *
116
+ * A second line of defence, not the first: a host's body-size guard should
117
+ * reject an oversized request before it is buffered. This catches a file that
118
+ * is individually too large inside a request that isn't.
119
+ *
120
+ * Caps are passed in rather than read here — they are deployment config, and a
121
+ * package that resolved them would pin every host to one source of truth.
122
+ */
123
+ export function requireUpload(form, field, { maxBytes, extensions, contentTypes, label = 'File' }) {
124
+ const file = form.get(field);
125
+ if (!(file instanceof File) || file.size === 0) {
126
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, `A ${label.toLowerCase()} is required`);
127
+ }
128
+ const extension = file.name.slice(file.name.lastIndexOf('.')).toLowerCase();
129
+ if (extensions && !extensions.includes(extension)) {
130
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, `File type not allowed. Allowed: ${extensions.join(', ')}`);
131
+ }
132
+ if (contentTypes && !contentTypes.allowed.has(file.type)) {
133
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, contentTypes.message);
134
+ }
135
+ if (file.size > maxBytes) {
136
+ apiError(400, ApiErrorCode.VALIDATION_FAILED, `${label} too large. Max size: ${maxBytes / (1024 * 1024)} MB`);
137
+ }
138
+ return { file, extension };
139
+ }
140
+ /** A multipart text field, trimmed and length-capped. Absent stays `undefined`. */
141
+ export function formText(form, field, { maxLength } = {}) {
142
+ const raw = form.get(field);
143
+ if (typeof raw !== 'string')
144
+ return undefined;
145
+ const trimmed = raw.trim();
146
+ if (!trimmed)
147
+ return undefined;
148
+ return maxLength ? trimmed.slice(0, maxLength) : trimmed;
149
+ }
150
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/api/request.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Turn a handler result — or a thrown `ApiError` — into a web-standard
3
+ * `Response`.
4
+ *
5
+ * This is the whole serialization boundary. A host framework that speaks
6
+ * `Request`/`Response` (SvelteKit, Next route handlers, Hono, Remix) needs only
7
+ * to build an `ApiRequest` and call `runHandler`; one that does not can reuse
8
+ * `toErrorBody` and serialize itself.
9
+ */
10
+ import { ApiError, ApiErrorCode } from './errors.js';
11
+ import type { ApiHandler, ApiRequest } from './types.js';
12
+ export interface ApiErrorBody {
13
+ message: string;
14
+ code: ApiErrorCode;
15
+ fields?: Record<string, string>;
16
+ }
17
+ export declare function toErrorBody(err: ApiError): ApiErrorBody;
18
+ /**
19
+ * Run a handler and serialize whatever comes out, including failures.
20
+ *
21
+ * `mapError` lets a host fold its own domain errors into the envelope before
22
+ * the 500 fallback — the Selva app maps `ProviderError`, `SchemaExtractionError`
23
+ * and `ComputeServerUnconfiguredError` this way, none of which belong here.
24
+ */
25
+ export declare function runHandler(handler: ApiHandler, req: ApiRequest, { fallback, mapError }: {
26
+ fallback: string;
27
+ mapError?: (err: unknown) => ApiError | undefined;
28
+ }): Promise<Response>;
29
+ //# sourceMappingURL=respond.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"respond.d.ts","sourceRoot":"","sources":["../../src/api/respond.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAc,MAAM,aAAa,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AAEtE,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,YAAY,CAIvD;AAcD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC/B,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE,UAAU,EACf,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,QAAQ,GAAG,SAAS,CAAA;CAAE,GAC7F,OAAO,CAAC,QAAQ,CAAC,CA0BnB"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Turn a handler result — or a thrown `ApiError` — into a web-standard
3
+ * `Response`.
4
+ *
5
+ * This is the whole serialization boundary. A host framework that speaks
6
+ * `Request`/`Response` (SvelteKit, Next route handlers, Hono, Remix) needs only
7
+ * to build an `ApiRequest` and call `runHandler`; one that does not can reuse
8
+ * `toErrorBody` and serialize itself.
9
+ */
10
+ import { ApiErrorCode, isApiError } from './errors.js';
11
+ export function toErrorBody(err) {
12
+ return err.fields
13
+ ? { message: err.message, code: err.code, fields: err.fields }
14
+ : { message: err.message, code: err.code };
15
+ }
16
+ function toResponse(result) {
17
+ if (result instanceof Response)
18
+ return result;
19
+ const { status, body, headers } = result;
20
+ if (body === undefined)
21
+ return new Response(null, { status: status ?? 204, headers });
22
+ return new Response(JSON.stringify(body), {
23
+ status: status ?? 200,
24
+ headers: { 'content-type': 'application/json', ...headers }
25
+ });
26
+ }
27
+ /**
28
+ * Run a handler and serialize whatever comes out, including failures.
29
+ *
30
+ * `mapError` lets a host fold its own domain errors into the envelope before
31
+ * the 500 fallback — the Selva app maps `ProviderError`, `SchemaExtractionError`
32
+ * and `ComputeServerUnconfiguredError` this way, none of which belong here.
33
+ */
34
+ export async function runHandler(handler, req, { fallback, mapError }) {
35
+ try {
36
+ return toResponse(await handler(req));
37
+ }
38
+ catch (err) {
39
+ if (isApiError(err)) {
40
+ return toResponse({ status: err.status, body: toErrorBody(err) });
41
+ }
42
+ const mapped = mapError?.(err);
43
+ if (mapped) {
44
+ return toResponse({ status: mapped.status, body: toErrorBody(mapped) });
45
+ }
46
+ // Never `console.error(…, err)`: provider adapters stash connection
47
+ // details on `cause`, and a raw console call hands the whole object to
48
+ // stdout where redaction never runs.
49
+ req.log.error(`[API] ${fallback}`, {
50
+ component: 'api',
51
+ err: err instanceof Error ? (err.stack ?? err.message) : String(err)
52
+ });
53
+ return toResponse({
54
+ status: 500,
55
+ body: { message: fallback, code: ApiErrorCode.INTERNAL }
56
+ });
57
+ }
58
+ }
59
+ //# sourceMappingURL=respond.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"respond.js","sourceRoot":"","sources":["../../src/api/respond.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAY,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AASjE,MAAM,UAAU,WAAW,CAAC,GAAa;IACxC,OAAO,GAAG,CAAC,MAAM;QAChB,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;QAC9D,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,SAAS,UAAU,CAAC,MAA8B;IACjD,IAAI,MAAM,YAAY,QAAQ;QAAE,OAAO,MAAM,CAAC;IAE9C,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IACzC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IAEtF,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE;QACzC,MAAM,EAAE,MAAM,IAAI,GAAG;QACrB,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,GAAG,OAAO,EAAE;KAC3D,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC/B,OAAmB,EACnB,GAAe,EACf,EAAE,QAAQ,EAAE,QAAQ,EAA2E;IAE/F,IAAI,CAAC;QACJ,OAAO,UAAU,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACnE,CAAC;QAED,MAAM,MAAM,GAAG,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;QAC/B,IAAI,MAAM,EAAE,CAAC;YACZ,OAAO,UAAU,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACzE,CAAC;QAED,oEAAoE;QACpE,uEAAuE;QACvE,qCAAqC;QACrC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,QAAQ,EAAE,EAAE;YAClC,SAAS,EAAE,KAAK;YAChB,GAAG,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACpE,CAAC,CAAC;QAEH,OAAO,UAAU,CAAC;YACjB,MAAM,EAAE,GAAG;YACX,IAAI,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,CAAC,QAAQ,EAAE;SACxD,CAAC,CAAC;IACJ,CAAC;AACF,CAAC"}