@gaganref/confect-core 10.3.1-fork.8 → 10.3.1-fork.81

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 (89) 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 +30 -93
  6. package/dist/App.d.ts.map +1 -1
  7. package/dist/App.js +24 -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/Component.d.ts +98 -2
  18. package/dist/Component.d.ts.map +1 -1
  19. package/dist/Component.js +44 -18
  20. package/dist/Component.js.map +1 -1
  21. package/dist/FunctionSpec.d.ts +365 -2
  22. package/dist/FunctionSpec.d.ts.map +1 -1
  23. package/dist/FunctionSpec.js +66 -3
  24. package/dist/FunctionSpec.js.map +1 -1
  25. package/dist/GenericId.js +2 -2
  26. package/dist/GroupSpec.js +1 -1
  27. package/dist/Http.d.ts +4 -8
  28. package/dist/Http.d.ts.map +1 -1
  29. package/dist/Http.js +15 -22
  30. package/dist/Http.js.map +1 -1
  31. package/dist/ManifestSection.d.ts +70 -0
  32. package/dist/ManifestSection.d.ts.map +1 -0
  33. package/dist/ManifestSection.js +7 -0
  34. package/dist/ManifestSection.js.map +1 -0
  35. package/dist/Mount.d.ts +200 -0
  36. package/dist/Mount.d.ts.map +1 -0
  37. package/dist/Mount.js +116 -0
  38. package/dist/Mount.js.map +1 -0
  39. package/dist/PaginationError.js +1 -1
  40. package/dist/Profile.d.ts +95 -0
  41. package/dist/Profile.d.ts.map +1 -0
  42. package/dist/Profile.js +15 -0
  43. package/dist/Profile.js.map +1 -0
  44. package/dist/Ref.d.ts +4 -3
  45. package/dist/Ref.d.ts.map +1 -1
  46. package/dist/Ref.js +13 -19
  47. package/dist/Ref.js.map +1 -1
  48. package/dist/RefInternal.d.ts +3 -0
  49. package/dist/RefInternal.d.ts.map +1 -0
  50. package/dist/RefInternal.js +16 -0
  51. package/dist/RefInternal.js.map +1 -0
  52. package/dist/SchemaToValidator.d.ts +11 -2
  53. package/dist/SchemaToValidator.d.ts.map +1 -1
  54. package/dist/SchemaToValidator.js +43 -14
  55. package/dist/SchemaToValidator.js.map +1 -1
  56. package/dist/SystemFields.js +3 -3
  57. package/dist/Table.d.ts +48 -9
  58. package/dist/Table.d.ts.map +1 -1
  59. package/dist/Table.js +41 -13
  60. package/dist/Table.js.map +1 -1
  61. package/dist/index.d.ts +4 -0
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +10 -6
  64. package/dist/internal/annotations.d.ts +2 -0
  65. package/dist/internal/annotations.d.ts.map +1 -0
  66. package/dist/internal/annotations.js +6 -0
  67. package/dist/internal/annotations.js.map +1 -0
  68. package/dist/internal/scalarDocs.d.ts +5 -0
  69. package/dist/internal/scalarDocs.d.ts.map +1 -0
  70. package/dist/internal/scalarDocs.js +43 -0
  71. package/dist/internal/scalarDocs.js.map +1 -0
  72. package/package.json +3 -3
  73. package/src/ActionCall.ts +25 -0
  74. package/src/App.ts +36 -136
  75. package/src/Auth.ts +54 -11
  76. package/src/Callback.ts +17 -4
  77. package/src/Component.ts +196 -11
  78. package/src/FunctionSpec.ts +566 -5
  79. package/src/Http.ts +15 -37
  80. package/src/ManifestSection.ts +78 -0
  81. package/src/Mount.ts +401 -0
  82. package/src/Profile.ts +141 -0
  83. package/src/Ref.ts +19 -26
  84. package/src/RefInternal.ts +26 -0
  85. package/src/SchemaToValidator.ts +47 -5
  86. package/src/Table.ts +157 -23
  87. package/src/index.ts +4 -0
  88. package/src/internal/annotations.ts +1 -0
  89. 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.8",
3
+ "version": "10.3.1-fork.81",
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,11 @@
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 MiddlewareSpec from "./MiddlewareSpec.js";
6
+ import * as Mount from "./Mount.js";
6
7
 
7
8
  const TypeId = "~@confect/core/App";
8
- const MountTypeId = "~@confect/core/App/Mount";
9
9
 
10
10
  /**
11
11
  * The default export of a Convex component's `convex.config` module.
@@ -19,112 +19,11 @@ export type HttpPrefix = `/${string}`;
19
19
  */
20
20
  export type Api = HttpApi.HttpApi<string, any>;
21
21
 
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
22
  type OwnerOf<V> = V extends Env.Entry<infer N, any, any, any> ? N : never;
122
23
 
123
24
  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
25
+ M extends Mount.Mount<any, any, Mount.Declared<any, infer B>>
26
+ ? { [K in keyof B]: OwnerOf<B[K]> }[keyof B]
128
27
  : never;
129
28
 
130
29
  type OwnedBy<N extends string, M> = {
@@ -137,51 +36,57 @@ export type Kind = "app" | "component";
137
36
 
138
37
  export interface App<
139
38
  E extends Env.Any,
140
- M extends Readonly<Record<string, AnyMount>>,
39
+ M extends ReadonlyArray<Mount.Any>,
141
40
  K extends Kind = Kind,
142
41
  > {
143
42
  readonly [TypeId]: typeof TypeId;
144
43
  readonly kind: K;
145
44
  readonly env: E;
45
+ readonly components: ReadonlyArray<Definition>;
146
46
  readonly mounts: M;
47
+ readonly requiresMiddleware: ReadonlyArray<MiddlewareSpec.AnyMiddlewareSpec>;
147
48
  }
148
49
 
149
- export type Any = App<Env.Any, Readonly<Record<string, AnyMount>>>;
50
+ export type Any = App<Env.Any, ReadonlyArray<Mount.Any>>;
150
51
 
151
52
  /**
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.
53
+ * Declare a backend, in `confect/app.ts`: its env, the `convex.config`
54
+ * definitions of the Convex components it mounts, and its mounts. Set `kind:
55
+ * "component"` when declaring a component; the default is `"app"`. Codegen
56
+ * rejects a kind that differs from its target, and writes
57
+ * `convex/convex.config.ts` with one `app.use` per mount, in list order, using
58
+ * the definition whose `defineComponent` name is the mount's component name. A
59
+ * mount that binds another backend's env entry fails to typecheck.
157
60
  *
158
61
  * ```ts
62
+ * import email from "@acme/email/convex.config";
63
+ * import workpool from "@convex-dev/workpool/convex.config";
64
+ *
159
65
  * export default App.make({
160
66
  * 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
- * },
67
+ * components: [email, workpool],
68
+ * mounts: [BulkEmail, TransactionalEmail, ImportsPool],
168
69
  * });
169
70
  * ```
170
71
  */
171
72
  export const make = <
172
73
  const E extends Env.Any,
173
- const M extends Readonly<Record<string, AnyMount>> = {},
74
+ const M extends ReadonlyArray<Mount.Any> = [],
174
75
  const K extends Kind = "app",
175
76
  >(
176
77
  options: {
177
78
  readonly env: E;
79
+ readonly requiresMiddleware?: ReadonlyArray<MiddlewareSpec.AnyMiddlewareSpec>;
80
+ readonly components?: ReadonlyArray<Definition>;
178
81
  readonly mounts?: M & OwnedBy<NoInfer<Env.Name<E>>, NoInfer<M>>;
179
82
  } & ([K] extends ["app"] ? { readonly kind?: K } : { readonly kind: K }),
180
83
  ): App<E, M, K> => ({
181
84
  [TypeId]: TypeId,
182
85
  kind: options.kind ?? ("app" as K),
183
86
  env: options.env,
184
- mounts: options.mounts ?? ({} as M),
87
+ components: options.components ?? [],
88
+ mounts: options.mounts ?? ([] as unknown as M),
89
+ requiresMiddleware: options.requiresMiddleware ?? [],
185
90
  });
186
91
 
187
92
  export const isApp = (u: unknown): u is Any =>
@@ -190,21 +95,16 @@ export const isApp = (u: unknown): u is Any =>
190
95
  Predicate.hasProperty(u, "kind") &&
191
96
  (u.kind === "app" || u.kind === "component");
192
97
 
193
- export const isMount = (u: unknown): u is AnyMount =>
194
- Predicate.hasProperty(u, MountTypeId);
195
-
196
98
  export const env = <A extends Any>(app: A): A["env"] => app.env;
197
99
 
198
100
  /**
199
- * Every mount with its name, in declaration order.
101
+ * The `convex.config` definitions the backend lists, in list order.
102
+ */
103
+ export const components = (app: Any): ReadonlyArray<Definition> =>
104
+ app.components;
105
+
106
+ /**
107
+ * What each mount was declared with, in list order.
200
108
  */
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
- }));
109
+ export const mounts = (app: Any): ReadonlyArray<Mount.Data> =>
110
+ 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;