@gaganref/confect-core 10.3.1-fork.9 → 10.3.1-fork.91

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 (94) hide show
  1. package/dist/ActionCall.d.ts +18 -0
  2. package/dist/ActionCall.d.ts.map +1 -0
  3. package/dist/ActionCall.js +21 -0
  4. package/dist/ActionCall.js.map +1 -0
  5. package/dist/App.d.ts +38 -93
  6. package/dist/App.d.ts.map +1 -1
  7. package/dist/App.js +30 -38
  8. package/dist/App.js.map +1 -1
  9. package/dist/Auth.d.ts +33 -9
  10. package/dist/Auth.d.ts.map +1 -1
  11. package/dist/Auth.js +17 -2
  12. package/dist/Auth.js.map +1 -1
  13. package/dist/Callback.d.ts +7 -2
  14. package/dist/Callback.d.ts.map +1 -1
  15. package/dist/Callback.js +11 -6
  16. package/dist/Callback.js.map +1 -1
  17. package/dist/CommitTs.d.ts +18 -0
  18. package/dist/CommitTs.d.ts.map +1 -0
  19. package/dist/CommitTs.js +20 -0
  20. package/dist/CommitTs.js.map +1 -0
  21. package/dist/Component.d.ts +106 -2
  22. package/dist/Component.d.ts.map +1 -1
  23. package/dist/Component.js +44 -18
  24. package/dist/Component.js.map +1 -1
  25. package/dist/FunctionSpec.d.ts +361 -2
  26. package/dist/FunctionSpec.d.ts.map +1 -1
  27. package/dist/FunctionSpec.js +68 -3
  28. package/dist/FunctionSpec.js.map +1 -1
  29. package/dist/GenericId.js +2 -2
  30. package/dist/GroupSpec.js +1 -1
  31. package/dist/Http.d.ts +4 -8
  32. package/dist/Http.d.ts.map +1 -1
  33. package/dist/Http.js +15 -22
  34. package/dist/Http.js.map +1 -1
  35. package/dist/ManifestSection.d.ts +101 -0
  36. package/dist/ManifestSection.d.ts.map +1 -0
  37. package/dist/ManifestSection.js +7 -0
  38. package/dist/ManifestSection.js.map +1 -0
  39. package/dist/Mount.d.ts +200 -0
  40. package/dist/Mount.d.ts.map +1 -0
  41. package/dist/Mount.js +116 -0
  42. package/dist/Mount.js.map +1 -0
  43. package/dist/PaginationError.js +1 -1
  44. package/dist/Profile.d.ts +95 -0
  45. package/dist/Profile.d.ts.map +1 -0
  46. package/dist/Profile.js +15 -0
  47. package/dist/Profile.js.map +1 -0
  48. package/dist/Ref.d.ts +4 -3
  49. package/dist/Ref.d.ts.map +1 -1
  50. package/dist/Ref.js +13 -19
  51. package/dist/Ref.js.map +1 -1
  52. package/dist/RefInternal.d.ts +3 -0
  53. package/dist/RefInternal.d.ts.map +1 -0
  54. package/dist/RefInternal.js +16 -0
  55. package/dist/RefInternal.js.map +1 -0
  56. package/dist/SchemaToValidator.d.ts +20 -6
  57. package/dist/SchemaToValidator.d.ts.map +1 -1
  58. package/dist/SchemaToValidator.js +45 -14
  59. package/dist/SchemaToValidator.js.map +1 -1
  60. package/dist/SystemFields.js +3 -3
  61. package/dist/Table.d.ts +48 -9
  62. package/dist/Table.d.ts.map +1 -1
  63. package/dist/Table.js +41 -13
  64. package/dist/Table.js.map +1 -1
  65. package/dist/index.d.ts +5 -0
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +11 -6
  68. package/dist/internal/annotations.d.ts +2 -0
  69. package/dist/internal/annotations.d.ts.map +1 -0
  70. package/dist/internal/annotations.js +6 -0
  71. package/dist/internal/annotations.js.map +1 -0
  72. package/dist/internal/scalarDocs.d.ts +5 -0
  73. package/dist/internal/scalarDocs.d.ts.map +1 -0
  74. package/dist/internal/scalarDocs.js +43 -0
  75. package/dist/internal/scalarDocs.js.map +1 -0
  76. package/package.json +3 -3
  77. package/src/ActionCall.ts +25 -0
  78. package/src/App.ts +45 -136
  79. package/src/Auth.ts +54 -11
  80. package/src/Callback.ts +17 -4
  81. package/src/CommitTs.ts +26 -0
  82. package/src/Component.ts +204 -11
  83. package/src/FunctionSpec.ts +562 -5
  84. package/src/Http.ts +15 -37
  85. package/src/ManifestSection.ts +108 -0
  86. package/src/Mount.ts +401 -0
  87. package/src/Profile.ts +141 -0
  88. package/src/Ref.ts +19 -26
  89. package/src/RefInternal.ts +26 -0
  90. package/src/SchemaToValidator.ts +97 -22
  91. package/src/Table.ts +157 -23
  92. package/src/index.ts +5 -0
  93. package/src/internal/annotations.ts +1 -0
  94. package/src/internal/scalarDocs.ts +72 -0
@@ -0,0 +1,43 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as HttpRouter from "effect/http/HttpRouter";
3
+ import * as HttpServerResponse from "effect/http/HttpServerResponse";
4
+ import * as OpenApi from "effect/http-api/OpenApi";
5
+ //#region src/internal/scalarDocs.ts
6
+ const escape = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
7
+ const escapeAttribute = (value) => escape(value).replaceAll("\"", "&quot;").replaceAll("'", "&#39;");
8
+ const escapeJson = (value) => JSON.stringify(value).replaceAll("<", "\\u003c").replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
9
+ const scalarDocs = (api, path) => HttpRouter.use((router) => {
10
+ let response;
11
+ return router.add("GET", path, Effect.sync(() => {
12
+ if (response !== void 0) return response;
13
+ const spec = OpenApi.fromApi(api);
14
+ const description = (tag) => !spec.info.description ? "" : `<meta name="${tag}" content="${escapeAttribute(spec.info.description)}"/>`;
15
+ response = HttpServerResponse.html(`<!doctype html>
16
+ <html>
17
+ <head>
18
+ <meta charset="utf-8" />
19
+ <title>${escape(spec.info.title)}</title>
20
+ ${description("description")}
21
+ ${description("og:description")}
22
+ <meta
23
+ name="viewport"
24
+ content="width=device-width, initial-scale=1" />
25
+ </head>
26
+ <body>
27
+ <div id="api-reference-container"></div>
28
+ <script src="${escapeAttribute("https://cdn.jsdelivr.net/npm/@scalar/api-reference@latest/dist/browser/standalone.min.js")}" crossorigin><\/script>
29
+ <script>
30
+ window.Scalar.createApiReference(document.getElementById('api-reference-container'), {
31
+ ...${escapeJson({ _integration: "html" })},
32
+ content: ${escapeJson(spec)}
33
+ })
34
+ <\/script>
35
+ </body>
36
+ </html>`);
37
+ return response;
38
+ }));
39
+ });
40
+ //#endregion
41
+ export { scalarDocs };
42
+
43
+ //# sourceMappingURL=scalarDocs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scalarDocs.js","names":[],"sources":["../../src/internal/scalarDocs.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\";\nimport * as HttpRouter from \"effect/http/HttpRouter\";\nimport * as HttpServerResponse from \"effect/http/HttpServerResponse\";\nimport type * as HttpApi from \"effect/http-api/HttpApi\";\nimport type * as HttpApiGroup from \"effect/http-api/HttpApiGroup\";\nimport * as OpenApi from \"effect/http-api/OpenApi\";\n\nconst escape = (value: string) =>\n value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\");\n\nconst escapeAttribute = (value: string) =>\n escape(value).replaceAll('\"', \"&quot;\").replaceAll(\"'\", \"&#39;\");\n\nconst escapeJson = (value: unknown) =>\n JSON.stringify(value)\n .replaceAll(\"<\", \"\\\\u003c\")\n .replaceAll(\"\\u2028\", \"\\\\u2028\")\n .replaceAll(\"\\u2029\", \"\\\\u2029\");\n\n// WORKAROUND: Effect 4.0.6's HttpApiScalar module imports its inline Scalar\n// script dynamically, and Convex's bundler then splits a schema that reaches it\n// into an imported chunk, which Convex refuses. This copies `layerCdn`'s page\n// byte for byte. Remove when `checkSchemaBundle.mjs` passes with\n// `HttpApiScalar.layerCdn` restored.\nexport const scalarDocs = <\n Id extends string,\n Groups extends HttpApiGroup.Constraint,\n>(\n api: HttpApi.HttpApi<Id, Groups>,\n path: `/${string}`,\n) =>\n HttpRouter.use((router) => {\n let response: HttpServerResponse.HttpServerResponse | undefined;\n return router.add(\n \"GET\",\n path,\n Effect.sync(() => {\n if (response !== undefined) return response;\n const spec = OpenApi.fromApi(api);\n const description = (tag: string) =>\n !spec.info.description\n ? \"\"\n : `<meta name=\"${tag}\" content=\"${escapeAttribute(spec.info.description)}\"/>`;\n response = HttpServerResponse.html(`<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n <title>${escape(spec.info.title)}</title>\n ${description(\"description\")}\n ${description(\"og:description\")}\n <meta\n name=\"viewport\"\n content=\"width=device-width, initial-scale=1\" />\n </head>\n <body>\n <div id=\"api-reference-container\"></div>\n <script src=\"${escapeAttribute(\"https://cdn.jsdelivr.net/npm/@scalar/api-reference@latest/dist/browser/standalone.min.js\")}\" crossorigin></script>\n <script>\n window.Scalar.createApiReference(document.getElementById('api-reference-container'), {\n ...${escapeJson({ _integration: \"html\" })},\n content: ${escapeJson(spec)}\n })\n </script>\n </body>\n</html>`);\n return response;\n }),\n );\n });\n"],"mappings":";;;;;AAOA,MAAM,UAAU,UACd,MACG,WAAW,KAAK,OAAO,CAAC,CACxB,WAAW,KAAK,MAAM,CAAC,CACvB,WAAW,KAAK,MAAM;AAE3B,MAAM,mBAAmB,UACvB,OAAO,KAAK,CAAC,CAAC,WAAW,MAAK,QAAQ,CAAC,CAAC,WAAW,KAAK,OAAO;AAEjE,MAAM,cAAc,UAClB,KAAK,UAAU,KAAK,CAAC,CAClB,WAAW,KAAK,SAAS,CAAC,CAC1B,WAAW,UAAU,SAAS,CAAC,CAC/B,WAAW,UAAU,SAAS;AAOnC,MAAa,cAIX,KACA,SAEA,WAAW,KAAK,WAAW;CACzB,IAAI;CACJ,OAAO,OAAO,IACZ,OACA,MACA,OAAO,WAAW;EAChB,IAAI,aAAa,KAAA,GAAW,OAAO;EACnC,MAAM,OAAO,QAAQ,QAAQ,GAAG;EAChC,MAAM,eAAe,QACnB,CAAC,KAAK,KAAK,cACP,KACA,eAAe,IAAI,aAAa,gBAAgB,KAAK,KAAK,WAAW,EAAE;EAC7E,WAAW,mBAAmB,KAAK;;;;aAI9B,OAAO,KAAK,KAAK,KAAK,EAAE;MAC/B,YAAY,aAAa,EAAE;MAC3B,YAAY,gBAAgB,EAAE;;;;;;;mBAOjB,gBAAgB,0FAA0F,EAAE;;;aAGlH,WAAW,EAAE,cAAc,OAAO,CAAC,EAAE;mBAC/B,WAAW,IAAI,EAAE;;;;QAI5B;EACA,OAAO;CACT,CAAC,CACH;AACF,CAAC"}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@gaganref/confect-core",
3
- "version": "10.3.1-fork.9",
3
+ "version": "10.3.1-fork.91",
4
4
  "peerDependencies": {
5
- "convex": "^1.46.0",
6
- "effect": "^4.0.0"
5
+ "convex": "^1.49.0",
6
+ "effect": ">=4.0.0 <4.1.0"
7
7
  },
8
8
  "description": "Shared specs and schemas used by all Confect packages",
9
9
  "author": "RJ Dellecese",
@@ -0,0 +1,25 @@
1
+ import * as Context from "effect/Context";
2
+ import type * as Effect from "effect/Effect";
3
+ import type * as Schema from "effect/Schema";
4
+ import * as FunctionSpec from "./FunctionSpec.js";
5
+ import * as MiddlewareSpec from "./MiddlewareSpec.js";
6
+ import type * as Ref from "./Ref.js";
7
+
8
+ export interface Call {
9
+ <Target extends Ref.AnyQuery | Ref.AnyMutation>(
10
+ target: Target,
11
+ ...args: Ref.OptionalArgs<NoInfer<Target>>
12
+ ): Effect.Effect<Ref.Returns<Target>, Ref.Error<Target> | Schema.SchemaError>;
13
+ }
14
+
15
+ export class ActionCall extends Context.Service<ActionCall, Call>()(
16
+ "@confect/core/ActionCall",
17
+ ) {}
18
+ export class Middleware extends MiddlewareSpec.MiddlewareSpec<
19
+ Middleware,
20
+ { provides: ActionCall }
21
+ >()("@confect/core/ActionCall/Middleware", {
22
+ functionTypes: { query: false, mutation: false, action: true },
23
+ }) {}
24
+
25
+ export const profile = FunctionSpec.extend()({ middleware: [Middleware] });
package/src/App.ts CHANGED
@@ -1,11 +1,12 @@
1
- import type { ComponentDefinition, EnvDefinition } from "convex/server";
2
- import type { Infer, Validator } from "convex/values";
1
+ import type { ComponentDefinition } from "convex/server";
3
2
  import type * as HttpApi from "effect/http-api/HttpApi";
4
3
  import * as Predicate from "effect/Predicate";
5
4
  import type * as Env from "./Env.js";
5
+ import type * as ManifestSection from "./ManifestSection.js";
6
+ import type * as MiddlewareSpec from "./MiddlewareSpec.js";
7
+ import * as Mount from "./Mount.js";
6
8
 
7
9
  const TypeId = "~@confect/core/App";
8
- const MountTypeId = "~@confect/core/App/Mount";
9
10
 
10
11
  /**
11
12
  * The default export of a Convex component's `convex.config` module.
@@ -19,112 +20,11 @@ export type HttpPrefix = `/${string}`;
19
20
  */
20
21
  export type Api = HttpApi.HttpApi<string, any>;
21
22
 
22
- type EnvOf<D extends Definition> = D["__env"];
23
-
24
- type IsOptional<V> = V extends Validator<any, "optional", any> ? true : false;
25
-
26
- type RequiredKeys<E extends EnvDefinition> = {
27
- [K in keyof E]: IsOptional<E[K]> extends true ? never : K;
28
- }[keyof E];
29
-
30
- type SlotValue<V extends Validator<any, any, any>> = Extract<Infer<V>, string>;
31
-
32
- /**
33
- * What one component env slot accepts: a constant, or an entry whose Convex
34
- * value fits. A required slot takes only a required entry.
35
- */
36
- type Slot<V extends Validator<any, any, any>> =
37
- IsOptional<V> extends true
38
- ? SlotValue<V> | undefined | Env.Entry<string, any, SlotValue<V>, boolean>
39
- : SlotValue<V> | Env.Entry<string, any, SlotValue<V>, false>;
40
-
41
- export type Bindings<E extends EnvDefinition> = {
42
- readonly [K in RequiredKeys<E>]: Slot<E[K]>;
43
- } & {
44
- readonly [K in Exclude<keyof E, RequiredKeys<E>>]?: Slot<E[K]>;
45
- };
46
-
47
- type HttpOptions =
48
- | { readonly httpPrefix?: never; readonly api?: never }
49
- | { readonly httpPrefix: HttpPrefix; readonly api?: Api };
50
-
51
- export type Options<D extends Definition> = HttpOptions &
52
- ([RequiredKeys<EnvOf<D>>] extends [never]
53
- ? { readonly env?: Bindings<EnvOf<D>> }
54
- : { readonly env: Bindings<EnvOf<D>> });
55
-
56
- type Exact<D extends Definition, O> = {
57
- readonly [K in Exclude<keyof O, "env" | "httpPrefix" | "api">]: never;
58
- } & (O extends { readonly env: infer B }
59
- ? {
60
- readonly env: { readonly [K in Exclude<keyof B, keyof EnvOf<D>>]: never };
61
- }
62
- : unknown);
63
-
64
- export interface MountData {
65
- readonly definition: Definition;
66
- readonly httpPrefix: HttpPrefix | undefined;
67
- readonly api: Api | undefined;
68
- readonly env: Readonly<Record<string, string | Env.AnyEntry | undefined>>;
69
- }
70
-
71
- /**
72
- * A mounted Convex component. `O` keeps the options as written, so {@link make}
73
- * can see which backends the bound entries come from.
74
- */
75
- export interface Mount<out D extends Definition, out O> extends MountData {
76
- readonly [MountTypeId]: { readonly definition: D; readonly options: O };
77
- readonly httpPrefix: O extends {
78
- readonly httpPrefix: infer P extends HttpPrefix;
79
- }
80
- ? P
81
- : undefined;
82
- }
83
-
84
- export type AnyMount = Mount<Definition, unknown>;
85
-
86
- /**
87
- * Mount a Convex component: the default export of its `convex.config` module,
88
- * official or Confect-authored. `env` is typed against the component's own
89
- * `defineComponent({ env })` declaration, so a missing required binding, an
90
- * optional entry in a required slot, a wrong literal and an unknown slot fail
91
- * to typecheck. `api` is the component's `HttpApi`, for route checks.
92
- */
93
- export function mount<const D extends Definition>(
94
- definition: D &
95
- ([RequiredKeys<EnvOf<D>>] extends [never]
96
- ? unknown
97
- : "App.mount: this component declares required env; pass { env }"),
98
- ): Mount<D, {}>;
99
- export function mount<const D extends Definition, const O extends Options<D>>(
100
- definition: D,
101
- options: O & Exact<NoInfer<D>, NoInfer<O>>,
102
- ): Mount<D, O>;
103
- export function mount(
104
- definition: Definition,
105
- options?: {
106
- readonly httpPrefix?: HttpPrefix;
107
- readonly api?: Api;
108
- readonly env?: MountData["env"];
109
- },
110
- ): AnyMount {
111
- return {
112
- [MountTypeId]: { definition, options },
113
- definition,
114
- httpPrefix: options?.httpPrefix,
115
- api: options?.api,
116
- env: options?.env ?? {},
117
- // The overloads above carry the precise types; this body serves both.
118
- } as AnyMount;
119
- }
120
-
121
23
  type OwnerOf<V> = V extends Env.Entry<infer N, any, any, any> ? N : never;
122
24
 
123
25
  type Owners<M> =
124
- M extends Mount<Definition, infer O>
125
- ? O extends { readonly env: infer B }
126
- ? { [K in keyof B]: OwnerOf<B[K]> }[keyof B]
127
- : never
26
+ M extends Mount.Mount<any, any, Mount.Declared<any, infer B>>
27
+ ? { [K in keyof B]: OwnerOf<B[K]> }[keyof B]
128
28
  : never;
129
29
 
130
30
  type OwnedBy<N extends string, M> = {
@@ -137,51 +37,65 @@ export type Kind = "app" | "component";
137
37
 
138
38
  export interface App<
139
39
  E extends Env.Any,
140
- M extends Readonly<Record<string, AnyMount>>,
40
+ M extends ReadonlyArray<Mount.Any>,
141
41
  K extends Kind = Kind,
142
42
  > {
143
43
  readonly [TypeId]: typeof TypeId;
144
44
  readonly kind: K;
145
45
  readonly env: E;
46
+ readonly components: ReadonlyArray<Definition>;
146
47
  readonly mounts: M;
48
+ readonly requiresMiddleware: ReadonlyArray<MiddlewareSpec.AnyMiddlewareSpec>;
49
+ readonly manifestSections: ReadonlyArray<ManifestSection.Entry>;
147
50
  }
148
51
 
149
- export type Any = App<Env.Any, Readonly<Record<string, AnyMount>>>;
52
+ export type Any = App<Env.Any, ReadonlyArray<Mount.Any>>;
150
53
 
151
54
  /**
152
- * Declare a backend, in `confect/app.ts`: its env and its mounted Convex
153
- * components by name. Set `kind: "component"` when declaring a component; the
154
- * default is `"app"`. Codegen rejects a kind that differs from its target.
155
- * Codegen writes `convex/convex.config.ts` from it. A mount that binds another
156
- * backend's env entry fails to typecheck.
55
+ * Declare a backend, in `confect/app.ts`: its env, the `convex.config`
56
+ * definitions of the Convex components it mounts, its mounts and its manifest
57
+ * sections. Set `kind: "component"` when declaring a component; the default is
58
+ * `"app"`. Codegen rejects a kind that differs from its target, and writes
59
+ * `convex/convex.config.ts` with one `app.use` per mount, in list order, using
60
+ * the definition whose `defineComponent` name is the mount's component name. A
61
+ * mount that binds another backend's env entry fails to typecheck.
62
+ *
63
+ * `manifestSections` is the only place codegen learns a backend's sections.
64
+ * Each section describes every registered function of an app, whatever builder
65
+ * made it, and writes its `_generated` file for an app or a component, even one
66
+ * with no functions.
157
67
  *
158
68
  * ```ts
69
+ * import email from "@acme/email/convex.config";
70
+ * import workpool from "@convex-dev/workpool/convex.config";
71
+ *
159
72
  * export default App.make({
160
73
  * env,
161
- * mounts: {
162
- * email: App.mount(email, {
163
- * httpPrefix: "/email",
164
- * env: { FROM: env.EMAIL_FROM, KEY: env.RESEND_API_KEY },
165
- * }),
166
- * importsPool: App.mount(workpool),
167
- * },
74
+ * components: [email, workpool],
75
+ * mounts: [BulkEmail, TransactionalEmail, ImportsPool],
168
76
  * });
169
77
  * ```
170
78
  */
171
79
  export const make = <
172
80
  const E extends Env.Any,
173
- const M extends Readonly<Record<string, AnyMount>> = {},
81
+ const M extends ReadonlyArray<Mount.Any> = [],
174
82
  const K extends Kind = "app",
175
83
  >(
176
84
  options: {
177
85
  readonly env: E;
86
+ readonly requiresMiddleware?: ReadonlyArray<MiddlewareSpec.AnyMiddlewareSpec>;
87
+ readonly manifestSections?: ReadonlyArray<ManifestSection.Entry>;
88
+ readonly components?: ReadonlyArray<Definition>;
178
89
  readonly mounts?: M & OwnedBy<NoInfer<Env.Name<E>>, NoInfer<M>>;
179
90
  } & ([K] extends ["app"] ? { readonly kind?: K } : { readonly kind: K }),
180
91
  ): App<E, M, K> => ({
181
92
  [TypeId]: TypeId,
182
93
  kind: options.kind ?? ("app" as K),
183
94
  env: options.env,
184
- mounts: options.mounts ?? ({} as M),
95
+ components: options.components ?? [],
96
+ mounts: options.mounts ?? ([] as unknown as M),
97
+ requiresMiddleware: options.requiresMiddleware ?? [],
98
+ manifestSections: options.manifestSections ?? [],
185
99
  });
186
100
 
187
101
  export const isApp = (u: unknown): u is Any =>
@@ -190,21 +104,16 @@ export const isApp = (u: unknown): u is Any =>
190
104
  Predicate.hasProperty(u, "kind") &&
191
105
  (u.kind === "app" || u.kind === "component");
192
106
 
193
- export const isMount = (u: unknown): u is AnyMount =>
194
- Predicate.hasProperty(u, MountTypeId);
195
-
196
107
  export const env = <A extends Any>(app: A): A["env"] => app.env;
197
108
 
198
109
  /**
199
- * Every mount with its name, in declaration order.
110
+ * The `convex.config` definitions the backend lists, in list order.
111
+ */
112
+ export const components = (app: Any): ReadonlyArray<Definition> =>
113
+ app.components;
114
+
115
+ /**
116
+ * What each mount was declared with, in list order.
200
117
  */
201
- export const mounts = (
202
- app: Any,
203
- ): ReadonlyArray<MountData & { readonly name: string }> =>
204
- Object.entries(app.mounts).map(([name, installed]) => ({
205
- name,
206
- definition: installed.definition,
207
- httpPrefix: installed.httpPrefix,
208
- api: installed.api,
209
- env: installed.env,
210
- }));
118
+ export const mounts = (app: Any): ReadonlyArray<Mount.Data> =>
119
+ app.mounts.map(Mount.data);
package/src/Auth.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  import type { AuthProvider } from "convex/server";
2
2
  import * as Predicate from "effect/Predicate";
3
3
  import type * as App from "./App.js";
4
+ import type * as Env from "./Env.js";
5
+ import * as Mount from "./Mount.js";
4
6
  import type { TypeError } from "./Types.js";
5
7
 
6
8
  const TypeId = "~@confect/core/Auth";
9
+ const CloudUrlTypeId = "~@confect/core/Auth/CloudUrl";
7
10
  const SiteUrlTypeId = "~@confect/core/Auth/SiteUrl";
8
11
 
9
12
  /**
@@ -15,19 +18,59 @@ export interface SiteUrl {
15
18
  readonly path: string;
16
19
  }
17
20
 
18
- type Url = string | SiteUrl;
21
+ /**
22
+ * The deployment's cloud URL (`CONVEX_CLOUD_URL`) plus a path.
23
+ */
24
+ export interface CloudUrl {
25
+ readonly [CloudUrlTypeId]: typeof CloudUrlTypeId;
26
+ readonly path: string;
27
+ }
19
28
 
20
- type WithSiteUrl<P> = P extends unknown
29
+ type Url =
30
+ | string
31
+ | SiteUrl
32
+ | CloudUrl
33
+ | Env.Entry<string, string, string, false>;
34
+
35
+ type WithUrls<P> = P extends unknown
21
36
  ? {
22
- [K in keyof P]: K extends "domain" | "issuer" | "jwks" ? Url : P[K];
37
+ [K in keyof P]: K extends "domain" | "issuer" | "jwks" | "applicationID"
38
+ ? Url
39
+ : P[K];
23
40
  }
24
41
  : never;
25
42
 
26
43
  /**
27
- * Convex's `AuthProvider`, with `domain`, `issuer` and `jwks` also taking a
28
- * {@link SiteUrl}.
44
+ * Convex's provider list with deployment URLs and required, public env strings.
45
+ */
46
+ export type Provider = WithUrls<AuthProvider> & {
47
+ /**
48
+ * Application-defined token family, copied to the runtime provider data.
49
+ */
50
+ readonly family?: string;
51
+ /**
52
+ * Application-defined issuer name, for example a trusted service.
53
+ */
54
+ readonly name?: string;
55
+ };
56
+
57
+ /**
58
+ * The cloud URL, or a path under it, resolved when Convex loads auth config.
29
59
  */
30
- export type Provider = WithSiteUrl<AuthProvider>;
60
+ export const cloudUrl = (path: `/${string}` | "" = ""): CloudUrl => ({
61
+ [CloudUrlTypeId]: CloudUrlTypeId,
62
+ path,
63
+ });
64
+
65
+ /**
66
+ * The path of a cloud URL placeholder, or `undefined` for another value.
67
+ */
68
+ export const cloudUrlPath = (u: unknown): string | undefined =>
69
+ Predicate.hasProperty(u, CloudUrlTypeId) &&
70
+ Predicate.hasProperty(u, "path") &&
71
+ typeof u.path === "string"
72
+ ? u.path
73
+ : undefined;
31
74
 
32
75
  export interface Auth<A extends App.Any> {
33
76
  readonly [TypeId]: typeof TypeId;
@@ -43,19 +86,19 @@ export type Any = Auth<App.Any>;
43
86
  */
44
87
  export function siteUrl(path?: `/${string}`): SiteUrl;
45
88
  export function siteUrl(
46
- mount: { readonly httpPrefix: App.HttpPrefix },
89
+ mount: Mount.Mount<any, string, Mount.Declared<App.HttpPrefix>>,
47
90
  path?: `/${string}`,
48
91
  ): SiteUrl;
49
92
  export function siteUrl(
50
- pathOrMount?: `/${string}` | { readonly httpPrefix: App.HttpPrefix },
93
+ pathOrMount?: `/${string}` | Mount.Any,
51
94
  path: string = "",
52
95
  ): SiteUrl {
53
96
  return {
54
97
  [SiteUrlTypeId]: SiteUrlTypeId,
55
98
  path:
56
- typeof pathOrMount === "object"
57
- ? pathOrMount.httpPrefix.replace(/\/+$/, "") + path
58
- : (pathOrMount ?? ""),
99
+ typeof pathOrMount === "string" || pathOrMount === undefined
100
+ ? (pathOrMount ?? "")
101
+ : (Mount.data(pathOrMount).httpPrefix ?? "").replace(/\/+$/, "") + path,
59
102
  };
60
103
  }
61
104
 
package/src/Callback.ts CHANGED
@@ -8,6 +8,8 @@ import * as Schema from "effect/Schema";
8
8
  import * as SchemaGetter from "effect/SchemaGetter";
9
9
  import * as FunctionProvenance from "./FunctionProvenance.js";
10
10
  import * as Ref from "./Ref.js";
11
+ import { getFunctionReference } from "./RefInternal.js";
12
+ import { CallbackContract } from "./internal/annotations.js";
11
13
  import type * as RuntimeAndFunctionType from "./RuntimeAndFunctionType.js";
12
14
 
13
15
  export const TypeId = "~@confect/core/Callback";
@@ -137,6 +139,7 @@ const isHandleOf =
137
139
  const fromText = <Contract_ extends Any>(
138
140
  contract: Contract_,
139
141
  text: string,
142
+ reservedArgument?: string,
140
143
  ): Handle<Contract_> =>
141
144
  Object.assign(
142
145
  Ref.fromProvenance(
@@ -145,25 +148,35 @@ const fromText = <Contract_ extends Any>(
145
148
  [],
146
149
  // Convex types a handle as a string that is also a function reference.
147
150
  text as unknown as FunctionReference<any, any>,
151
+ contract.functionType,
152
+ contract.functionType === "query" ? undefined : reservedArgument,
148
153
  ),
149
154
  { [HandleTypeId]: contract.id },
150
155
  ) as unknown as Handle<Contract_>;
151
156
 
152
157
  /**
153
158
  * The schema of a handle for `contract`: a string on the wire, a runnable ref
154
- * once decoded. Use it in a component's function args or tables.
159
+ * once decoded. Use it in a component's function args or tables. It lowers to
160
+ * Convex's `v.function` with the contract's encoded args and returns.
161
+ * `reservedArgument` opts mutation and action handles into context forwarding.
162
+ * The host target must reserve the same argument name.
155
163
  */
156
164
  export const handle = <Contract_ extends Any>(
157
165
  contract: Contract_,
166
+ options?: {
167
+ readonly reservedArgument?: Contract_["functionType"] extends "query"
168
+ ? never
169
+ : string;
170
+ },
158
171
  ): Schema.Codec<Handle<Contract_>, string> =>
159
- Schema.String.pipe(
172
+ Schema.String.annotate({ [CallbackContract]: contract }).pipe(
160
173
  Schema.decodeTo(
161
174
  Schema.declare(isHandleOf(contract), {
162
175
  identifier: `Callback.Handle<${contract.id}>`,
163
176
  }),
164
177
  {
165
178
  decode: SchemaGetter.transform((text: string) =>
166
- fromText(contract, text),
179
+ fromText(contract, text, options?.reservedArgument),
167
180
  ),
168
181
  encode: SchemaGetter.transform(
169
182
  (ref: Handle<Contract_>) => ref.convexFunctionName,
@@ -301,4 +314,4 @@ export const toFuture = <Ref_ extends Ref.Any>(
301
314
  Ref.GetFunctionVisibility<Ref_>,
302
315
  EncodedArgs<Ref_>,
303
316
  EncodedReturns<Ref_>
304
- > => Ref.getFunctionReference(ref) as never;
317
+ > => getFunctionReference(ref) as never;
@@ -0,0 +1,26 @@
1
+ import { CommitTsPlaceholder } from "convex/values";
2
+ import * as Schema from "effect/Schema";
3
+ import * as SchemaAST from "effect/SchemaAST";
4
+
5
+ const ConvexCommitTs = "~@confect/core/ConvexCommitTs";
6
+
7
+ /**
8
+ * A commit timestamp as Convex types it. A mutation writes the database
9
+ * writer's `vars.commitTs` placeholder, which resolves to an int64 when the
10
+ * mutation commits. Reading the field back inside that mutation still yields
11
+ * the placeholder, so a reader narrows with `typeof value === "bigint"` before
12
+ * using it as a number.
13
+ */
14
+ export type CommitTs = bigint | CommitTsPlaceholder;
15
+
16
+ /**
17
+ * A field Convex stores as `v.commitTs()`. An index on it orders documents by
18
+ * commit order.
19
+ */
20
+ export const CommitTs: Schema.Codec<CommitTs> = Schema.Union([
21
+ Schema.BigInt,
22
+ Schema.instanceOf(CommitTsPlaceholder),
23
+ ]).annotate({ [ConvexCommitTs]: true });
24
+
25
+ export const isCommitTs = (ast: SchemaAST.AST): boolean =>
26
+ SchemaAST.resolveAt<boolean>(ConvexCommitTs)(ast) === true;