@gaganref/confect-core 10.3.1-fork.66 → 10.3.1-fork.67
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.
- package/dist/App.d.ts +3 -8
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +0 -2
- package/dist/App.js.map +1 -1
- package/dist/Http.d.ts +4 -8
- package/dist/Http.d.ts.map +1 -1
- package/dist/Http.js +12 -19
- package/dist/Http.js.map +1 -1
- package/package.json +1 -1
- package/src/App.ts +3 -11
- package/src/Http.ts +13 -32
package/dist/App.d.ts
CHANGED
|
@@ -31,11 +31,7 @@ export type Bindings<E extends EnvDefinition> = {
|
|
|
31
31
|
readonly [K in Exclude<keyof E, RequiredKeys<E>>]?: Slot<E[K]>;
|
|
32
32
|
};
|
|
33
33
|
type HttpOptions = {
|
|
34
|
-
readonly httpPrefix?:
|
|
35
|
-
readonly api?: never;
|
|
36
|
-
} | {
|
|
37
|
-
readonly httpPrefix: HttpPrefix;
|
|
38
|
-
readonly api?: Api;
|
|
34
|
+
readonly httpPrefix?: HttpPrefix;
|
|
39
35
|
};
|
|
40
36
|
export type Options<D extends Definition> = HttpOptions & {
|
|
41
37
|
/**
|
|
@@ -50,7 +46,7 @@ export type Options<D extends Definition> = HttpOptions & {
|
|
|
50
46
|
readonly env: Bindings<EnvOf<D>>;
|
|
51
47
|
});
|
|
52
48
|
type Exact<D extends Definition, O> = {
|
|
53
|
-
readonly [K in Exclude<keyof O, "env" | "httpPrefix" | "
|
|
49
|
+
readonly [K in Exclude<keyof O, "env" | "httpPrefix" | "testing">]: never;
|
|
54
50
|
} & (O extends {
|
|
55
51
|
readonly env: infer B;
|
|
56
52
|
} ? {
|
|
@@ -61,7 +57,6 @@ type Exact<D extends Definition, O> = {
|
|
|
61
57
|
export interface MountData {
|
|
62
58
|
readonly definition: Definition;
|
|
63
59
|
readonly httpPrefix: HttpPrefix | undefined;
|
|
64
|
-
readonly api: Api | undefined;
|
|
65
60
|
readonly env: Readonly<Record<string, string | Env.AnyEntry | undefined>>;
|
|
66
61
|
readonly testing: string | undefined;
|
|
67
62
|
}
|
|
@@ -84,7 +79,7 @@ export type AnyMount = Mount<Definition, unknown>;
|
|
|
84
79
|
* official or Confect-authored. `env` is typed against the component's own
|
|
85
80
|
* `defineComponent({ env })` declaration, so a missing required binding, an
|
|
86
81
|
* optional entry in a required slot, a wrong literal and an unknown slot fail
|
|
87
|
-
* to typecheck.
|
|
82
|
+
* to typecheck.
|
|
88
83
|
*/
|
|
89
84
|
export declare function mount<const D extends Definition>(definition: D & ([RequiredKeys<EnvOf<D>>] extends [never] ? unknown : "App.mount: this component declares required env; pass { env }")): Mount<D, {}>;
|
|
90
85
|
export declare function mount<const D extends Definition, const O extends Options<D>>(definition: D, options: O & Exact<NoInfer<D>, NoInfer<O>>): Mount<D, O>;
|
package/dist/App.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,KAAK,OAAO,MAAM,yBAAyB,CAAC;AAExD,OAAO,KAAK,KAAK,GAAG,MAAM,UAAU,CAAC;AACrC,OAAO,KAAK,KAAK,cAAc,MAAM,qBAAqB,CAAC;AAE3D,QAAA,MAAM,MAAM,uBAAuB,CAAC;AACpC,QAAA,MAAM,WAAW,6BAA6B,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,mBAAmB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAEvD,MAAM,MAAM,UAAU,GAAG,IAAI,MAAM,EAAE,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE/C,KAAK,KAAK,CAAC,CAAC,SAAS,UAAU,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;AAE9C,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC;AAE9E,KAAK,YAAY,CAAC,CAAC,SAAS,aAAa,IAAI;KAC1C,CAAC,IAAI,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,KAAK,GAAG,CAAC;CAC1D,CAAC,MAAM,CAAC,CAAC,CAAC;AAEX,KAAK,SAAS,CAAC,CAAC,SAAS,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;AAE/E;;;GAGG;AACH,KAAK,IAAI,CAAC,CAAC,SAAS,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAC1C,UAAU,CAAC,CAAC,CAAC,SAAS,IAAI,GACtB,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,GACxE,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAEjE,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,aAAa,IAAI;IAC9C,QAAQ,EAAE,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC5C,GAAG;IACF,QAAQ,EAAE,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/D,CAAC;AAEF,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.ts"],"names":[],"mappings":"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"}
|
package/dist/App.js
CHANGED
|
@@ -19,7 +19,6 @@ function mount(definition, options) {
|
|
|
19
19
|
},
|
|
20
20
|
definition,
|
|
21
21
|
httpPrefix: options?.httpPrefix,
|
|
22
|
-
api: options?.api,
|
|
23
22
|
env: options?.env ?? {},
|
|
24
23
|
testing: options?.testing
|
|
25
24
|
};
|
|
@@ -61,7 +60,6 @@ const mounts = (app) => Object.entries(app.mounts).map(([name, installed]) => ({
|
|
|
61
60
|
name,
|
|
62
61
|
definition: installed.definition,
|
|
63
62
|
httpPrefix: installed.httpPrefix,
|
|
64
|
-
api: installed.api,
|
|
65
63
|
env: installed.env,
|
|
66
64
|
testing: installed.testing
|
|
67
65
|
}));
|
package/dist/App.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.js","names":[],"sources":["../src/App.ts"],"sourcesContent":["import type { ComponentDefinition, EnvDefinition } from \"convex/server\";\nimport type { Infer, Validator } from \"convex/values\";\nimport type * as HttpApi from \"effect/http-api/HttpApi\";\nimport * as Predicate from \"effect/Predicate\";\nimport type * as Env from \"./Env.js\";\nimport type * as MiddlewareSpec from \"./MiddlewareSpec.js\";\n\nconst TypeId = \"~@confect/core/App\";\nconst MountTypeId = \"~@confect/core/App/Mount\";\n\n/**\n * The default export of a Convex component's `convex.config` module.\n */\nexport type Definition = ComponentDefinition<any, any>;\n\nexport type HttpPrefix = `/${string}`;\n\n/**\n * Any `HttpApi`, with its identifier and groups erased.\n */\nexport type Api = HttpApi.HttpApi<string, any>;\n\ntype EnvOf<D extends Definition> = D[\"__env\"];\n\ntype IsOptional<V> = V extends Validator<any, \"optional\", any> ? true : false;\n\ntype RequiredKeys<E extends EnvDefinition> = {\n [K in keyof E]: IsOptional<E[K]> extends true ? never : K;\n}[keyof E];\n\ntype SlotValue<V extends Validator<any, any, any>> = Extract<Infer<V>, string>;\n\n/**\n * What one component env slot accepts: a constant, or an entry whose Convex\n * value fits. A required slot takes only a required entry.\n */\ntype Slot<V extends Validator<any, any, any>> =\n IsOptional<V> extends true\n ? SlotValue<V> | undefined | Env.Entry<string, any, SlotValue<V>, boolean>\n : SlotValue<V> | Env.Entry<string, any, SlotValue<V>, false>;\n\nexport type Bindings<E extends EnvDefinition> = {\n readonly [K in RequiredKeys<E>]: Slot<E[K]>;\n} & {\n readonly [K in Exclude<keyof E, RequiredKeys<E>>]?: Slot<E[K]>;\n};\n\ntype HttpOptions
|
|
1
|
+
{"version":3,"file":"App.js","names":[],"sources":["../src/App.ts"],"sourcesContent":["import type { ComponentDefinition, EnvDefinition } from \"convex/server\";\nimport type { Infer, Validator } from \"convex/values\";\nimport type * as HttpApi from \"effect/http-api/HttpApi\";\nimport * as Predicate from \"effect/Predicate\";\nimport type * as Env from \"./Env.js\";\nimport type * as MiddlewareSpec from \"./MiddlewareSpec.js\";\n\nconst TypeId = \"~@confect/core/App\";\nconst MountTypeId = \"~@confect/core/App/Mount\";\n\n/**\n * The default export of a Convex component's `convex.config` module.\n */\nexport type Definition = ComponentDefinition<any, any>;\n\nexport type HttpPrefix = `/${string}`;\n\n/**\n * Any `HttpApi`, with its identifier and groups erased.\n */\nexport type Api = HttpApi.HttpApi<string, any>;\n\ntype EnvOf<D extends Definition> = D[\"__env\"];\n\ntype IsOptional<V> = V extends Validator<any, \"optional\", any> ? true : false;\n\ntype RequiredKeys<E extends EnvDefinition> = {\n [K in keyof E]: IsOptional<E[K]> extends true ? never : K;\n}[keyof E];\n\ntype SlotValue<V extends Validator<any, any, any>> = Extract<Infer<V>, string>;\n\n/**\n * What one component env slot accepts: a constant, or an entry whose Convex\n * value fits. A required slot takes only a required entry.\n */\ntype Slot<V extends Validator<any, any, any>> =\n IsOptional<V> extends true\n ? SlotValue<V> | undefined | Env.Entry<string, any, SlotValue<V>, boolean>\n : SlotValue<V> | Env.Entry<string, any, SlotValue<V>, false>;\n\nexport type Bindings<E extends EnvDefinition> = {\n readonly [K in RequiredKeys<E>]: Slot<E[K]>;\n} & {\n readonly [K in Exclude<keyof E, RequiredKeys<E>>]?: Slot<E[K]>;\n};\n\ntype HttpOptions = { readonly httpPrefix?: HttpPrefix };\n\nexport type Options<D extends Definition> = HttpOptions & {\n /**\n * The package specifier of the module whose `register(t, name)` the generated\n * test registry calls for this mount, in place of the component package's own\n * `<package>/test`. The kit registers the mount's children.\n */\n readonly testing?: string;\n} & ([RequiredKeys<EnvOf<D>>] extends [never]\n ? { readonly env?: Bindings<EnvOf<D>> }\n : { readonly env: Bindings<EnvOf<D>> });\n\ntype Exact<D extends Definition, O> = {\n readonly [K in Exclude<keyof O, \"env\" | \"httpPrefix\" | \"testing\">]: never;\n} & (O extends { readonly env: infer B }\n ? {\n readonly env: { readonly [K in Exclude<keyof B, keyof EnvOf<D>>]: never };\n }\n : unknown);\n\nexport interface MountData {\n readonly definition: Definition;\n readonly httpPrefix: HttpPrefix | undefined;\n readonly env: Readonly<Record<string, string | Env.AnyEntry | undefined>>;\n readonly testing: string | undefined;\n}\n\n/**\n * A mounted Convex component. `O` keeps the options as written, so {@link make}\n * can see which backends the bound entries come from.\n */\nexport interface Mount<out D extends Definition, out O> extends MountData {\n readonly [MountTypeId]: { readonly definition: D; readonly options: O };\n readonly httpPrefix: O extends {\n readonly httpPrefix: infer P extends HttpPrefix;\n }\n ? P\n : undefined;\n}\n\nexport type AnyMount = Mount<Definition, unknown>;\n\n/**\n * Mount a Convex component: the default export of its `convex.config` module,\n * official or Confect-authored. `env` is typed against the component's own\n * `defineComponent({ env })` declaration, so a missing required binding, an\n * optional entry in a required slot, a wrong literal and an unknown slot fail\n * to typecheck.\n */\nexport function mount<const D extends Definition>(\n definition: D &\n ([RequiredKeys<EnvOf<D>>] extends [never]\n ? unknown\n : \"App.mount: this component declares required env; pass { env }\"),\n): Mount<D, {}>;\nexport function mount<const D extends Definition, const O extends Options<D>>(\n definition: D,\n options: O & Exact<NoInfer<D>, NoInfer<O>>,\n): Mount<D, O>;\nexport function mount(\n definition: Definition,\n options?: {\n readonly httpPrefix?: HttpPrefix;\n readonly env?: MountData[\"env\"];\n readonly testing?: string;\n },\n): AnyMount {\n return {\n [MountTypeId]: { definition, options },\n definition,\n httpPrefix: options?.httpPrefix,\n env: options?.env ?? {},\n testing: options?.testing,\n // The overloads above carry the precise types; this body serves both.\n } as AnyMount;\n}\n\ntype OwnerOf<V> = V extends Env.Entry<infer N, any, any, any> ? N : never;\n\ntype Owners<M> =\n M extends Mount<Definition, infer O>\n ? O extends { readonly env: infer B }\n ? { [K in keyof B]: OwnerOf<B[K]> }[keyof B]\n : never\n : never;\n\ntype OwnedBy<N extends string, M> = {\n readonly [K in keyof M]: [Exclude<Owners<M[K]>, N>] extends [never]\n ? unknown\n : { readonly foreignEnvBackend: Exclude<Owners<M[K]>, N> };\n};\n\nexport type Kind = \"app\" | \"component\";\n\nexport interface App<\n E extends Env.Any,\n M extends Readonly<Record<string, AnyMount>>,\n K extends Kind = Kind,\n> {\n readonly [TypeId]: typeof TypeId;\n readonly kind: K;\n readonly env: E;\n readonly mounts: M;\n readonly requiresMiddleware: ReadonlyArray<MiddlewareSpec.AnyMiddlewareSpec>;\n}\n\nexport type Any = App<Env.Any, Readonly<Record<string, AnyMount>>>;\n\n/**\n * Declare a backend, in `confect/app.ts`: its env and its mounted Convex\n * components by name. Set `kind: \"component\"` when declaring a component; the\n * default is `\"app\"`. Codegen rejects a kind that differs from its target.\n * Codegen writes `convex/convex.config.ts` from it. A mount that binds another\n * backend's env entry fails to typecheck.\n *\n * ```ts\n * export default App.make({\n * env,\n * mounts: {\n * email: App.mount(email, {\n * httpPrefix: \"/email\",\n * env: { FROM: env.EMAIL_FROM, KEY: env.RESEND_API_KEY },\n * }),\n * importsPool: App.mount(workpool),\n * },\n * });\n * ```\n */\nexport const make = <\n const E extends Env.Any,\n const M extends Readonly<Record<string, AnyMount>> = {},\n const K extends Kind = \"app\",\n>(\n options: {\n readonly env: E;\n readonly requiresMiddleware?: ReadonlyArray<MiddlewareSpec.AnyMiddlewareSpec>;\n readonly mounts?: M & OwnedBy<NoInfer<Env.Name<E>>, NoInfer<M>>;\n } & ([K] extends [\"app\"] ? { readonly kind?: K } : { readonly kind: K }),\n): App<E, M, K> => ({\n [TypeId]: TypeId,\n kind: options.kind ?? (\"app\" as K),\n env: options.env,\n mounts: options.mounts ?? ({} as M),\n requiresMiddleware: options.requiresMiddleware ?? [],\n});\n\nexport const isApp = (u: unknown): u is Any =>\n Predicate.hasProperty(u, TypeId) &&\n u[TypeId] === TypeId &&\n Predicate.hasProperty(u, \"kind\") &&\n (u.kind === \"app\" || u.kind === \"component\");\n\nexport const isMount = (u: unknown): u is AnyMount =>\n Predicate.hasProperty(u, MountTypeId);\n\nexport const env = <A extends Any>(app: A): A[\"env\"] => app.env;\n\n/**\n * Every mount with its name, in declaration order.\n */\nexport const mounts = (\n app: Any,\n): ReadonlyArray<MountData & { readonly name: string }> =>\n Object.entries(app.mounts).map(([name, installed]) => ({\n name,\n definition: installed.definition,\n httpPrefix: installed.httpPrefix,\n env: installed.env,\n testing: installed.testing,\n }));\n"],"mappings":";;;;;;;;;;;AAOA,MAAM,SAAS;AACf,MAAM,cAAc;AAmGpB,SAAgB,MACd,YACA,SAKU;CACV,OAAO;GACJ,cAAc;GAAE;GAAY;EAAQ;EACrC;EACA,YAAY,SAAS;EACrB,KAAK,SAAS,OAAO,CAAC;EACtB,SAAS,SAAS;CAEpB;AACF;;;;;;;;;;;;;;;;;;;;;AAqDA,MAAa,QAKX,aAKkB;EACjB,SAAS;CACV,MAAM,QAAQ,QAAS;CACvB,KAAK,QAAQ;CACb,QAAQ,QAAQ,UAAW,CAAC;CAC5B,oBAAoB,QAAQ,sBAAsB,CAAC;AACrD;AAEA,MAAa,SAAS,MACpB,UAAU,YAAY,GAAG,MAAM,KAC/B,EAAE,YAAY,UACd,UAAU,YAAY,GAAG,MAAM,MAC9B,EAAE,SAAS,SAAS,EAAE,SAAS;AAElC,MAAa,WAAW,MACtB,UAAU,YAAY,GAAG,WAAW;AAEtC,MAAa,OAAsB,QAAqB,IAAI;;;;AAK5D,MAAa,UACX,QAEA,OAAO,QAAQ,IAAI,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,gBAAgB;CACrD;CACA,YAAY,UAAU;CACtB,YAAY,UAAU;CACtB,KAAK,UAAU;CACf,SAAS,UAAU;AACrB,EAAE"}
|
package/dist/Http.d.ts
CHANGED
|
@@ -42,9 +42,6 @@ export type Owner = {
|
|
|
42
42
|
readonly kind: "app";
|
|
43
43
|
} | {
|
|
44
44
|
readonly kind: "docs";
|
|
45
|
-
} | {
|
|
46
|
-
readonly kind: "mount";
|
|
47
|
-
readonly mount: string;
|
|
48
45
|
};
|
|
49
46
|
export interface Route {
|
|
50
47
|
readonly method: string;
|
|
@@ -90,11 +87,10 @@ export declare class OverlappingMountPrefixes extends OverlappingMountPrefixes_b
|
|
|
90
87
|
}
|
|
91
88
|
export type RouteError = RouteUnderMountPrefix | DuplicateRoute | OverlappingMountPrefixes;
|
|
92
89
|
/**
|
|
93
|
-
*
|
|
94
|
-
* mount's
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* when two mount prefixes overlap.
|
|
90
|
+
* The backend's own HTTP routes and docs page. Fails when a route is at or
|
|
91
|
+
* under a mount's prefix (Convex accepts that and lets the app route silently
|
|
92
|
+
* shadow the component's), when two routes share a method and path, or when two
|
|
93
|
+
* mount prefixes overlap.
|
|
98
94
|
*/
|
|
99
95
|
export declare const routes: (app: App.Any, http?: Any) => Result.Result<ReadonlyArray<Route>, RouteError>;
|
|
100
96
|
export {};
|
package/dist/Http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Http.d.ts","sourceRoot":"","sources":["../src/Http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,KAAK,OAAO,MAAM,yBAAyB,CAAC;AAExD,OAAO,KAAK,KAAK,YAAY,MAAM,8BAA8B,CAAC;AAElE,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC,QAAA,MAAM,MAAM,wBAAwB,CAAC;AAErC;;;;;;;;GAQG;AACH,MAAM,WAAW,IAAI,CAAC,GAAG,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,SAAS,KAAK,CAAC,GAAG;IACjE,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,MAAM,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB,QAAQ,CAAC,IAAI,EAAE,IAAI,MAAM,EAAE,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,IAAI,GACf,EAAE,SAAS,MAAM,EACjB,MAAM,SAAS,YAAY,CAAC,UAAU,EACtC,CAAC,WACQ;IACT,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,MAAM,EAAE,CAAC;CAC9B,knBAeA,CAAC;AAEF,eAAO,MAAM,MAAM,MAAO,OAAO,KAAG,CAAC,IAAI,GACiB,CAAC;AAE3D,MAAM,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"Http.d.ts","sourceRoot":"","sources":["../src/Http.ts"],"names":[],"mappings":"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"}
|
package/dist/Http.js
CHANGED
|
@@ -47,7 +47,7 @@ var RouteUnderMountPrefix = class extends Data.TaggedError("RouteUnderMountPrefi
|
|
|
47
47
|
};
|
|
48
48
|
var DuplicateRoute = class extends Data.TaggedError("DuplicateRoute") {
|
|
49
49
|
get message() {
|
|
50
|
-
return `${this.method} ${this.path} is declared twice (${
|
|
50
|
+
return `${this.method} ${this.path} is declared twice (${this.first.kind} and ${this.second.kind}).`;
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
53
|
var OverlappingMountPrefixes = class extends Data.TaggedError("OverlappingMountPrefixes") {
|
|
@@ -55,7 +55,6 @@ var OverlappingMountPrefixes = class extends Data.TaggedError("OverlappingMountP
|
|
|
55
55
|
return `The mounts "${this.first.mount}" (${this.first.httpPrefix}) and "${this.second.mount}" (${this.second.httpPrefix}) have overlapping HTTP prefixes.`;
|
|
56
56
|
}
|
|
57
57
|
};
|
|
58
|
-
const ownerLabel = (owner) => owner.kind === "mount" ? `mount "${owner.mount}"` : owner.kind;
|
|
59
58
|
const trimSlash = (prefix) => prefix.length > 1 && prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
60
59
|
const isUnder = (path, prefix) => prefix === "/" || path === prefix || path === `${prefix}/` || path.startsWith(`${prefix}/`);
|
|
61
60
|
/**
|
|
@@ -69,8 +68,8 @@ const paramNames = (params) => {
|
|
|
69
68
|
while (ast.encoding !== void 0) ast = ast.encoding[ast.encoding.length - 1].to;
|
|
70
69
|
return SchemaAST.isObjects(ast) ? new Set(ast.propertySignatures.map(({ name }) => String(name))) : /* @__PURE__ */ new Set();
|
|
71
70
|
};
|
|
72
|
-
const apiRoutes = (api,
|
|
73
|
-
const path =
|
|
71
|
+
const apiRoutes = (api, owner) => Object.values(api.groups).flatMap((group) => Object.values(group.endpoints).map((endpoint) => {
|
|
72
|
+
const path = endpoint.path;
|
|
74
73
|
const declared = paramNames(endpoint.params);
|
|
75
74
|
return {
|
|
76
75
|
route: {
|
|
@@ -82,17 +81,15 @@ const apiRoutes = (api, prefix, owner) => Object.values(api.groups).flatMap((gro
|
|
|
82
81
|
};
|
|
83
82
|
}));
|
|
84
83
|
/**
|
|
85
|
-
*
|
|
86
|
-
* mount's
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
* when two mount prefixes overlap.
|
|
84
|
+
* The backend's own HTTP routes and docs page. Fails when a route is at or
|
|
85
|
+
* under a mount's prefix (Convex accepts that and lets the app route silently
|
|
86
|
+
* shadow the component's), when two routes share a method and path, or when two
|
|
87
|
+
* mount prefixes overlap.
|
|
90
88
|
*/
|
|
91
89
|
const routes = (app, http) => {
|
|
92
|
-
const mounts$1 = mounts(app).flatMap(({ name, httpPrefix
|
|
90
|
+
const mounts$1 = mounts(app).flatMap(({ name, httpPrefix }) => httpPrefix === void 0 ? [] : [{
|
|
93
91
|
name,
|
|
94
|
-
httpPrefix: trimSlash(httpPrefix)
|
|
95
|
-
api
|
|
92
|
+
httpPrefix: trimSlash(httpPrefix)
|
|
96
93
|
}]);
|
|
97
94
|
for (const [index, first] of mounts$1.entries()) for (const second of mounts$1.slice(index + 1)) if (isUnder(second.httpPrefix, first.httpPrefix) || isUnder(first.httpPrefix, second.httpPrefix)) return Result.fail(new OverlappingMountPrefixes({
|
|
98
95
|
first: {
|
|
@@ -104,7 +101,7 @@ const routes = (app, http) => {
|
|
|
104
101
|
httpPrefix: second.httpPrefix
|
|
105
102
|
}
|
|
106
103
|
}));
|
|
107
|
-
const own = http === void 0 ? [] : [...apiRoutes(http.api,
|
|
104
|
+
const own = http === void 0 ? [] : [...apiRoutes(http.api, { kind: "app" }), ...http.docs === void 0 ? [] : [{
|
|
108
105
|
route: {
|
|
109
106
|
method: "GET",
|
|
110
107
|
path: http.docs,
|
|
@@ -121,12 +118,8 @@ const routes = (app, http) => {
|
|
|
121
118
|
httpPrefix: mount.httpPrefix
|
|
122
119
|
}));
|
|
123
120
|
}
|
|
124
|
-
const all = [...own, ...mounts$1.flatMap(({ name, httpPrefix, api }) => api === void 0 ? [] : apiRoutes(api, httpPrefix, {
|
|
125
|
-
kind: "mount",
|
|
126
|
-
mount: name
|
|
127
|
-
}))];
|
|
128
121
|
const seen = /* @__PURE__ */ new Map();
|
|
129
|
-
for (const { route, key } of
|
|
122
|
+
for (const { route, key } of own) {
|
|
130
123
|
const previous = seen.get(key);
|
|
131
124
|
if (previous !== void 0) return Result.fail(new DuplicateRoute({
|
|
132
125
|
method: route.method,
|
|
@@ -136,7 +129,7 @@ const routes = (app, http) => {
|
|
|
136
129
|
}));
|
|
137
130
|
seen.set(key, route);
|
|
138
131
|
}
|
|
139
|
-
return Result.succeed(
|
|
132
|
+
return Result.succeed(own.map(({ route }) => route));
|
|
140
133
|
};
|
|
141
134
|
//#endregion
|
|
142
135
|
export { DuplicateRoute, Http_exports, OverlappingMountPrefixes, RouteUnderMountPrefix, isHttp, make, routes };
|
package/dist/Http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Http.js","names":["mounts","App.mounts"],"sources":["../src/Http.ts"],"sourcesContent":["import * as Data from \"effect/Data\";\nimport * as Layer from \"effect/Layer\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Result from \"effect/Result\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaAST from \"effect/SchemaAST\";\nimport type * as HttpApi from \"effect/http-api/HttpApi\";\nimport * as HttpApiBuilder from \"effect/http-api/HttpApiBuilder\";\nimport type * as HttpApiGroup from \"effect/http-api/HttpApiGroup\";\nimport { scalarDocs } from \"./internal/scalarDocs.js\";\nimport * as App from \"./App.js\";\n\nconst TypeId = \"~@confect/core/Http\";\n\n/**\n * A backend's HTTP declaration: the app's own `HttpApi`, its handlers, and an\n * optional Scalar docs path. `routes` is the layer the generated\n * `convex/http.ts` serves. Mounted components serve their own routes.\n *\n * It does not reference `confect/app.ts`: `http.ts` runs in every HTTP action,\n * and `app.ts` imports component definitions that only load in Convex's config\n * analysis. Codegen checks these routes against the app's mounts.\n */\nexport interface Http<Api extends App.Api, Routes extends Layer.Any> {\n readonly [TypeId]: typeof TypeId;\n readonly api: Api;\n readonly docs: `/${string}` | undefined;\n readonly routes: Routes;\n}\n\nexport type Any = Http<App.Api, Layer.Any>;\n\n/**\n * Declare the backend's HTTP API, in `confect/http.ts`. Every route is an\n * `HttpApi` endpoint. `docs` serves a Scalar page for the api at that path.\n *\n * ```ts\n * export default Http.make({\n * api: TenantApi,\n * handlers: TenantHandlers,\n * docs: \"/docs\",\n * });\n * ```\n */\nexport const make = <\n Id extends string,\n Groups extends HttpApiGroup.Constraint,\n R,\n>(options: {\n readonly api: HttpApi.HttpApi<Id, Groups>;\n readonly handlers: Layer.Layer<HttpApiGroup.ToService<Id, Groups>, never, R>;\n readonly docs?: `/${string}`;\n}) => {\n const api = HttpApiBuilder.layer(options.api).pipe(\n Layer.provide(options.handlers),\n );\n const routes =\n options.docs === undefined\n ? api\n : Layer.merge(api, scalarDocs(options.api, options.docs));\n const http: Http<HttpApi.HttpApi<Id, Groups>, typeof routes> = {\n [TypeId]: TypeId,\n api: options.api,\n docs: options.docs,\n routes,\n };\n return http;\n};\n\nexport const isHttp = (u: unknown): u is Any =>\n Predicate.hasProperty(u, TypeId) && u[TypeId] === TypeId;\n\nexport type Owner =\n | { readonly kind: \"app\" }\n | { readonly kind: \"docs\" }\n | { readonly kind: \"mount\"; readonly mount: string };\n\nexport interface Route {\n readonly method: string;\n readonly path: string;\n readonly owner: Owner;\n}\n\nexport class RouteUnderMountPrefix extends Data.TaggedError(\n \"RouteUnderMountPrefix\",\n)<{\n readonly method: string;\n readonly path: string;\n readonly mount: string;\n readonly httpPrefix: string;\n}> {\n override get message(): string {\n return `The app route ${this.method} ${this.path} is under the HTTP prefix ${this.httpPrefix} of the mount \"${this.mount}\". Convex would let it shadow the component's routes; move it or change the prefix.`;\n }\n}\n\nexport class DuplicateRoute extends Data.TaggedError(\"DuplicateRoute\")<{\n readonly method: string;\n readonly path: string;\n readonly first: Owner;\n readonly second: Owner;\n}> {\n override get message(): string {\n return `${this.method} ${this.path} is declared twice (${ownerLabel(this.first)} and ${ownerLabel(this.second)}).`;\n }\n}\n\nexport class OverlappingMountPrefixes extends Data.TaggedError(\n \"OverlappingMountPrefixes\",\n)<{\n readonly first: { readonly mount: string; readonly httpPrefix: string };\n readonly second: { readonly mount: string; readonly httpPrefix: string };\n}> {\n override get message(): string {\n return `The mounts \"${this.first.mount}\" (${this.first.httpPrefix}) and \"${this.second.mount}\" (${this.second.httpPrefix}) have overlapping HTTP prefixes.`;\n }\n}\n\nexport type RouteError =\n | RouteUnderMountPrefix\n | DuplicateRoute\n | OverlappingMountPrefixes;\n\nconst ownerLabel = (owner: Owner): string =>\n owner.kind === \"mount\" ? `mount \"${owner.mount}\"` : owner.kind;\n\nconst trimSlash = (prefix: string): string =>\n prefix.length > 1 && prefix.endsWith(\"/\") ? prefix.slice(0, -1) : prefix;\n\nconst isUnder = (path: string, prefix: string): boolean =>\n prefix === \"/\" ||\n path === prefix ||\n path === `${prefix}/` ||\n path.startsWith(`${prefix}/`);\n\n/**\n * The parameter names an endpoint declares. As in Effect's router, a colon\n * segment is a parameter only when its name is declared; otherwise it is\n * literal text.\n */\nconst paramNames = (params: Schema.Top | undefined): ReadonlySet<string> => {\n if (params === undefined) return new Set();\n let ast = params.ast;\n while (ast.encoding !== undefined) {\n ast = ast.encoding[ast.encoding.length - 1].to;\n }\n return SchemaAST.isObjects(ast)\n ? new Set(ast.propertySignatures.map(({ name }) => String(name)))\n : new Set();\n};\n\ninterface KeyedRoute {\n readonly route: Route;\n /**\n * Method and path with declared parameter names erased.\n */\n readonly key: string;\n}\n\nconst apiRoutes = (\n api: App.Api,\n prefix: string,\n owner: Owner,\n): ReadonlyArray<KeyedRoute> =>\n Object.values<HttpApiGroup.Top>(api.groups).flatMap((group) =>\n Object.values(group.endpoints).map((endpoint) => {\n const path = prefix + endpoint.path;\n const declared = paramNames(endpoint.params);\n return {\n route: { method: endpoint.method, path, owner },\n key: `${endpoint.method} ${path.replaceAll(/:(\\w+)/g, (param, name: string) => (declared.has(name) ? \":\" : param))}`,\n };\n }),\n );\n\n/**\n * Every HTTP route the backend serves: the app's api, its docs page and each\n * mount's api under its prefix. Fails when an app route or the docs page is at\n * or under a mount's prefix (Convex accepts that and lets the app route\n * silently shadow the component's), when two routes share a method and path, or\n * when two mount prefixes overlap.\n */\nexport const routes = (\n app: App.Any,\n http?: Any,\n): Result.Result<ReadonlyArray<Route>, RouteError> => {\n const mounts = App.mounts(app).flatMap(({ name, httpPrefix, api }) =>\n httpPrefix === undefined\n ? []\n : [{ name, httpPrefix: trimSlash(httpPrefix), api }],\n );\n\n for (const [index, first] of mounts.entries()) {\n for (const second of mounts.slice(index + 1)) {\n if (\n isUnder(second.httpPrefix, first.httpPrefix) ||\n isUnder(first.httpPrefix, second.httpPrefix)\n ) {\n return Result.fail(\n new OverlappingMountPrefixes({\n first: { mount: first.name, httpPrefix: first.httpPrefix },\n second: { mount: second.name, httpPrefix: second.httpPrefix },\n }),\n );\n }\n }\n }\n\n const own: ReadonlyArray<KeyedRoute> =\n http === undefined\n ? []\n : [\n ...apiRoutes(http.api, \"\", { kind: \"app\" }),\n ...(http.docs === undefined\n ? []\n : [\n {\n route: {\n method: \"GET\",\n path: http.docs,\n owner: { kind: \"docs\" } as const,\n },\n key: `GET ${http.docs}`,\n },\n ]),\n ];\n\n for (const { route } of own) {\n const mount = mounts.find(({ httpPrefix }) =>\n isUnder(route.path, httpPrefix),\n );\n if (mount !== undefined) {\n return Result.fail(\n new RouteUnderMountPrefix({\n method: route.method,\n path: route.path,\n mount: mount.name,\n httpPrefix: mount.httpPrefix,\n }),\n );\n }\n }\n\n const all = [\n ...own,\n ...mounts.flatMap(({ name, httpPrefix, api }) =>\n api === undefined\n ? []\n : apiRoutes(api, httpPrefix, { kind: \"mount\", mount: name }),\n ),\n ];\n const seen = new Map<string, Route>();\n for (const { route, key } of all) {\n const previous = seen.get(key);\n if (previous !== undefined) {\n return Result.fail(\n new DuplicateRoute({\n method: route.method,\n path: route.path,\n first: previous.owner,\n second: route.owner,\n }),\n );\n }\n seen.set(key, route);\n }\n return Result.succeed(all.map(({ route }) => route));\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Http.js","names":["mounts","App.mounts"],"sources":["../src/Http.ts"],"sourcesContent":["import * as Data from \"effect/Data\";\nimport * as Layer from \"effect/Layer\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Result from \"effect/Result\";\nimport type * as Schema from \"effect/Schema\";\nimport * as SchemaAST from \"effect/SchemaAST\";\nimport type * as HttpApi from \"effect/http-api/HttpApi\";\nimport * as HttpApiBuilder from \"effect/http-api/HttpApiBuilder\";\nimport type * as HttpApiGroup from \"effect/http-api/HttpApiGroup\";\nimport { scalarDocs } from \"./internal/scalarDocs.js\";\nimport * as App from \"./App.js\";\n\nconst TypeId = \"~@confect/core/Http\";\n\n/**\n * A backend's HTTP declaration: the app's own `HttpApi`, its handlers, and an\n * optional Scalar docs path. `routes` is the layer the generated\n * `convex/http.ts` serves. Mounted components serve their own routes.\n *\n * It does not reference `confect/app.ts`: `http.ts` runs in every HTTP action,\n * and `app.ts` imports component definitions that only load in Convex's config\n * analysis. Codegen checks these routes against the app's mounts.\n */\nexport interface Http<Api extends App.Api, Routes extends Layer.Any> {\n readonly [TypeId]: typeof TypeId;\n readonly api: Api;\n readonly docs: `/${string}` | undefined;\n readonly routes: Routes;\n}\n\nexport type Any = Http<App.Api, Layer.Any>;\n\n/**\n * Declare the backend's HTTP API, in `confect/http.ts`. Every route is an\n * `HttpApi` endpoint. `docs` serves a Scalar page for the api at that path.\n *\n * ```ts\n * export default Http.make({\n * api: TenantApi,\n * handlers: TenantHandlers,\n * docs: \"/docs\",\n * });\n * ```\n */\nexport const make = <\n Id extends string,\n Groups extends HttpApiGroup.Constraint,\n R,\n>(options: {\n readonly api: HttpApi.HttpApi<Id, Groups>;\n readonly handlers: Layer.Layer<HttpApiGroup.ToService<Id, Groups>, never, R>;\n readonly docs?: `/${string}`;\n}) => {\n const api = HttpApiBuilder.layer(options.api).pipe(\n Layer.provide(options.handlers),\n );\n const routes =\n options.docs === undefined\n ? api\n : Layer.merge(api, scalarDocs(options.api, options.docs));\n const http: Http<HttpApi.HttpApi<Id, Groups>, typeof routes> = {\n [TypeId]: TypeId,\n api: options.api,\n docs: options.docs,\n routes,\n };\n return http;\n};\n\nexport const isHttp = (u: unknown): u is Any =>\n Predicate.hasProperty(u, TypeId) && u[TypeId] === TypeId;\n\nexport type Owner = { readonly kind: \"app\" } | { readonly kind: \"docs\" };\n\nexport interface Route {\n readonly method: string;\n readonly path: string;\n readonly owner: Owner;\n}\n\nexport class RouteUnderMountPrefix extends Data.TaggedError(\n \"RouteUnderMountPrefix\",\n)<{\n readonly method: string;\n readonly path: string;\n readonly mount: string;\n readonly httpPrefix: string;\n}> {\n override get message(): string {\n return `The app route ${this.method} ${this.path} is under the HTTP prefix ${this.httpPrefix} of the mount \"${this.mount}\". Convex would let it shadow the component's routes; move it or change the prefix.`;\n }\n}\n\nexport class DuplicateRoute extends Data.TaggedError(\"DuplicateRoute\")<{\n readonly method: string;\n readonly path: string;\n readonly first: Owner;\n readonly second: Owner;\n}> {\n override get message(): string {\n return `${this.method} ${this.path} is declared twice (${this.first.kind} and ${this.second.kind}).`;\n }\n}\n\nexport class OverlappingMountPrefixes extends Data.TaggedError(\n \"OverlappingMountPrefixes\",\n)<{\n readonly first: { readonly mount: string; readonly httpPrefix: string };\n readonly second: { readonly mount: string; readonly httpPrefix: string };\n}> {\n override get message(): string {\n return `The mounts \"${this.first.mount}\" (${this.first.httpPrefix}) and \"${this.second.mount}\" (${this.second.httpPrefix}) have overlapping HTTP prefixes.`;\n }\n}\n\nexport type RouteError =\n | RouteUnderMountPrefix\n | DuplicateRoute\n | OverlappingMountPrefixes;\n\nconst trimSlash = (prefix: string): string =>\n prefix.length > 1 && prefix.endsWith(\"/\") ? prefix.slice(0, -1) : prefix;\n\nconst isUnder = (path: string, prefix: string): boolean =>\n prefix === \"/\" ||\n path === prefix ||\n path === `${prefix}/` ||\n path.startsWith(`${prefix}/`);\n\n/**\n * The parameter names an endpoint declares. As in Effect's router, a colon\n * segment is a parameter only when its name is declared; otherwise it is\n * literal text.\n */\nconst paramNames = (params: Schema.Top | undefined): ReadonlySet<string> => {\n if (params === undefined) return new Set();\n let ast = params.ast;\n while (ast.encoding !== undefined) {\n ast = ast.encoding[ast.encoding.length - 1].to;\n }\n return SchemaAST.isObjects(ast)\n ? new Set(ast.propertySignatures.map(({ name }) => String(name)))\n : new Set();\n};\n\ninterface KeyedRoute {\n readonly route: Route;\n /**\n * Method and path with declared parameter names erased.\n */\n readonly key: string;\n}\n\nconst apiRoutes = (api: App.Api, owner: Owner): ReadonlyArray<KeyedRoute> =>\n Object.values<HttpApiGroup.Top>(api.groups).flatMap((group) =>\n Object.values(group.endpoints).map((endpoint) => {\n const path = endpoint.path;\n const declared = paramNames(endpoint.params);\n return {\n route: { method: endpoint.method, path, owner },\n key: `${endpoint.method} ${path.replaceAll(/:(\\w+)/g, (param, name: string) => (declared.has(name) ? \":\" : param))}`,\n };\n }),\n );\n\n/**\n * The backend's own HTTP routes and docs page. Fails when a route is at or\n * under a mount's prefix (Convex accepts that and lets the app route silently\n * shadow the component's), when two routes share a method and path, or when two\n * mount prefixes overlap.\n */\nexport const routes = (\n app: App.Any,\n http?: Any,\n): Result.Result<ReadonlyArray<Route>, RouteError> => {\n const mounts = App.mounts(app).flatMap(({ name, httpPrefix }) =>\n httpPrefix === undefined\n ? []\n : [{ name, httpPrefix: trimSlash(httpPrefix) }],\n );\n\n for (const [index, first] of mounts.entries()) {\n for (const second of mounts.slice(index + 1)) {\n if (\n isUnder(second.httpPrefix, first.httpPrefix) ||\n isUnder(first.httpPrefix, second.httpPrefix)\n ) {\n return Result.fail(\n new OverlappingMountPrefixes({\n first: { mount: first.name, httpPrefix: first.httpPrefix },\n second: { mount: second.name, httpPrefix: second.httpPrefix },\n }),\n );\n }\n }\n }\n\n const own: ReadonlyArray<KeyedRoute> =\n http === undefined\n ? []\n : [\n ...apiRoutes(http.api, { kind: \"app\" }),\n ...(http.docs === undefined\n ? []\n : [\n {\n route: {\n method: \"GET\",\n path: http.docs,\n owner: { kind: \"docs\" } as const,\n },\n key: `GET ${http.docs}`,\n },\n ]),\n ];\n\n for (const { route } of own) {\n const mount = mounts.find(({ httpPrefix }) =>\n isUnder(route.path, httpPrefix),\n );\n if (mount !== undefined) {\n return Result.fail(\n new RouteUnderMountPrefix({\n method: route.method,\n path: route.path,\n mount: mount.name,\n httpPrefix: mount.httpPrefix,\n }),\n );\n }\n }\n\n const seen = new Map<string, Route>();\n for (const { route, key } of own) {\n const previous = seen.get(key);\n if (previous !== undefined) {\n return Result.fail(\n new DuplicateRoute({\n method: route.method,\n path: route.path,\n first: previous.owner,\n second: route.owner,\n }),\n );\n }\n seen.set(key, route);\n }\n return Result.succeed(own.map(({ route }) => route));\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAYA,MAAM,SAAS;;;;;;;;;;;;;AAgCf,MAAa,QAIX,YAII;CACJ,MAAM,MAAM,eAAe,MAAM,QAAQ,GAAG,CAAC,CAAC,KAC5C,MAAM,QAAQ,QAAQ,QAAQ,CAChC;CACA,MAAM,SACJ,QAAQ,SAAS,KAAA,IACb,MACA,MAAM,MAAM,KAAK,WAAW,QAAQ,KAAK,QAAQ,IAAI,CAAC;CAO5D,OAAO;GALJ,SAAS;EACV,KAAK,QAAQ;EACb,MAAM,QAAQ;EACd;CAEQ;AACZ;AAEA,MAAa,UAAU,MACrB,UAAU,YAAY,GAAG,MAAM,KAAK,EAAE,YAAY;AAUpD,IAAa,wBAAb,cAA2C,KAAK,YAC9C,uBACF,CAAC,CAKE;CACD,IAAa,UAAkB;EAC7B,OAAO,iBAAiB,KAAK,OAAO,GAAG,KAAK,KAAK,4BAA4B,KAAK,WAAW,iBAAiB,KAAK,MAAM;CAC3H;AACF;AAEA,IAAa,iBAAb,cAAoC,KAAK,YAAY,gBAAgB,CAAC,CAKnE;CACD,IAAa,UAAkB;EAC7B,OAAO,GAAG,KAAK,OAAO,GAAG,KAAK,KAAK,sBAAsB,KAAK,MAAM,KAAK,OAAO,KAAK,OAAO,KAAK;CACnG;AACF;AAEA,IAAa,2BAAb,cAA8C,KAAK,YACjD,0BACF,CAAC,CAGE;CACD,IAAa,UAAkB;EAC7B,OAAO,eAAe,KAAK,MAAM,MAAM,KAAK,KAAK,MAAM,WAAW,SAAS,KAAK,OAAO,MAAM,KAAK,KAAK,OAAO,WAAW;CAC3H;AACF;AAOA,MAAM,aAAa,WACjB,OAAO,SAAS,KAAK,OAAO,SAAS,GAAG,IAAI,OAAO,MAAM,GAAG,EAAE,IAAI;AAEpE,MAAM,WAAW,MAAc,WAC7B,WAAW,OACX,SAAS,UACT,SAAS,GAAG,OAAO,MACnB,KAAK,WAAW,GAAG,OAAO,EAAE;;;;;;AAO9B,MAAM,cAAc,WAAwD;CAC1E,IAAI,WAAW,KAAA,GAAW,uBAAO,IAAI,IAAI;CACzC,IAAI,MAAM,OAAO;CACjB,OAAO,IAAI,aAAa,KAAA,GACtB,MAAM,IAAI,SAAS,IAAI,SAAS,SAAS,EAAE,CAAC;CAE9C,OAAO,UAAU,UAAU,GAAG,IAC1B,IAAI,IAAI,IAAI,mBAAmB,KAAK,EAAE,WAAW,OAAO,IAAI,CAAC,CAAC,oBAC9D,IAAI,IAAI;AACd;AAUA,MAAM,aAAa,KAAc,UAC/B,OAAO,OAAyB,IAAI,MAAM,CAAC,CAAC,SAAS,UACnD,OAAO,OAAO,MAAM,SAAS,CAAC,CAAC,KAAK,aAAa;CAC/C,MAAM,OAAO,SAAS;CACtB,MAAM,WAAW,WAAW,SAAS,MAAM;CAC3C,OAAO;EACL,OAAO;GAAE,QAAQ,SAAS;GAAQ;GAAM;EAAM;EAC9C,KAAK,GAAG,SAAS,OAAO,GAAG,KAAK,WAAW,YAAY,OAAO,SAAkB,SAAS,IAAI,IAAI,IAAI,MAAM,KAAM;CACnH;AACF,CAAC,CACH;;;;;;;AAQF,MAAa,UACX,KACA,SACoD;CACpD,MAAMA,WAASC,OAAW,GAAG,CAAC,CAAC,SAAS,EAAE,MAAM,iBAC9C,eAAe,KAAA,IACX,CAAC,IACD,CAAC;EAAE;EAAM,YAAY,UAAU,UAAU;CAAE,CAAC,CAClD;CAEA,KAAK,MAAM,CAAC,OAAO,UAAUD,SAAO,QAAQ,GAC1C,KAAK,MAAM,UAAUA,SAAO,MAAM,QAAQ,CAAC,GACzC,IACE,QAAQ,OAAO,YAAY,MAAM,UAAU,KAC3C,QAAQ,MAAM,YAAY,OAAO,UAAU,GAE3C,OAAO,OAAO,KACZ,IAAI,yBAAyB;EAC3B,OAAO;GAAE,OAAO,MAAM;GAAM,YAAY,MAAM;EAAW;EACzD,QAAQ;GAAE,OAAO,OAAO;GAAM,YAAY,OAAO;EAAW;CAC9D,CAAC,CACH;CAKN,MAAM,MACJ,SAAS,KAAA,IACL,CAAC,IACD,CACE,GAAG,UAAU,KAAK,KAAK,EAAE,MAAM,MAAM,CAAC,GACtC,GAAI,KAAK,SAAS,KAAA,IACd,CAAC,IACD,CACE;EACE,OAAO;GACL,QAAQ;GACR,MAAM,KAAK;GACX,OAAO,EAAE,MAAM,OAAO;EACxB;EACA,KAAK,OAAO,KAAK;CACnB,CACF,CACN;CAEN,KAAK,MAAM,EAAE,WAAW,KAAK;EAC3B,MAAM,QAAQA,SAAO,MAAM,EAAE,iBAC3B,QAAQ,MAAM,MAAM,UAAU,CAChC;EACA,IAAI,UAAU,KAAA,GACZ,OAAO,OAAO,KACZ,IAAI,sBAAsB;GACxB,QAAQ,MAAM;GACd,MAAM,MAAM;GACZ,OAAO,MAAM;GACb,YAAY,MAAM;EACpB,CAAC,CACH;CAEJ;CAEA,MAAM,uBAAO,IAAI,IAAmB;CACpC,KAAK,MAAM,EAAE,OAAO,SAAS,KAAK;EAChC,MAAM,WAAW,KAAK,IAAI,GAAG;EAC7B,IAAI,aAAa,KAAA,GACf,OAAO,OAAO,KACZ,IAAI,eAAe;GACjB,QAAQ,MAAM;GACd,MAAM,MAAM;GACZ,OAAO,SAAS;GAChB,QAAQ,MAAM;EAChB,CAAC,CACH;EAEF,KAAK,IAAI,KAAK,KAAK;CACrB;CACA,OAAO,OAAO,QAAQ,IAAI,KAAK,EAAE,YAAY,KAAK,CAAC;AACrD"}
|
package/package.json
CHANGED
package/src/App.ts
CHANGED
|
@@ -45,9 +45,7 @@ export type Bindings<E extends EnvDefinition> = {
|
|
|
45
45
|
readonly [K in Exclude<keyof E, RequiredKeys<E>>]?: Slot<E[K]>;
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
-
type HttpOptions =
|
|
49
|
-
| { readonly httpPrefix?: never; readonly api?: never }
|
|
50
|
-
| { readonly httpPrefix: HttpPrefix; readonly api?: Api };
|
|
48
|
+
type HttpOptions = { readonly httpPrefix?: HttpPrefix };
|
|
51
49
|
|
|
52
50
|
export type Options<D extends Definition> = HttpOptions & {
|
|
53
51
|
/**
|
|
@@ -61,9 +59,7 @@ export type Options<D extends Definition> = HttpOptions & {
|
|
|
61
59
|
: { readonly env: Bindings<EnvOf<D>> });
|
|
62
60
|
|
|
63
61
|
type Exact<D extends Definition, O> = {
|
|
64
|
-
readonly [
|
|
65
|
-
K in Exclude<keyof O, "env" | "httpPrefix" | "api" | "testing">
|
|
66
|
-
]: never;
|
|
62
|
+
readonly [K in Exclude<keyof O, "env" | "httpPrefix" | "testing">]: never;
|
|
67
63
|
} & (O extends { readonly env: infer B }
|
|
68
64
|
? {
|
|
69
65
|
readonly env: { readonly [K in Exclude<keyof B, keyof EnvOf<D>>]: never };
|
|
@@ -73,7 +69,6 @@ type Exact<D extends Definition, O> = {
|
|
|
73
69
|
export interface MountData {
|
|
74
70
|
readonly definition: Definition;
|
|
75
71
|
readonly httpPrefix: HttpPrefix | undefined;
|
|
76
|
-
readonly api: Api | undefined;
|
|
77
72
|
readonly env: Readonly<Record<string, string | Env.AnyEntry | undefined>>;
|
|
78
73
|
readonly testing: string | undefined;
|
|
79
74
|
}
|
|
@@ -98,7 +93,7 @@ export type AnyMount = Mount<Definition, unknown>;
|
|
|
98
93
|
* official or Confect-authored. `env` is typed against the component's own
|
|
99
94
|
* `defineComponent({ env })` declaration, so a missing required binding, an
|
|
100
95
|
* optional entry in a required slot, a wrong literal and an unknown slot fail
|
|
101
|
-
* to typecheck.
|
|
96
|
+
* to typecheck.
|
|
102
97
|
*/
|
|
103
98
|
export function mount<const D extends Definition>(
|
|
104
99
|
definition: D &
|
|
@@ -114,7 +109,6 @@ export function mount(
|
|
|
114
109
|
definition: Definition,
|
|
115
110
|
options?: {
|
|
116
111
|
readonly httpPrefix?: HttpPrefix;
|
|
117
|
-
readonly api?: Api;
|
|
118
112
|
readonly env?: MountData["env"];
|
|
119
113
|
readonly testing?: string;
|
|
120
114
|
},
|
|
@@ -123,7 +117,6 @@ export function mount(
|
|
|
123
117
|
[MountTypeId]: { definition, options },
|
|
124
118
|
definition,
|
|
125
119
|
httpPrefix: options?.httpPrefix,
|
|
126
|
-
api: options?.api,
|
|
127
120
|
env: options?.env ?? {},
|
|
128
121
|
testing: options?.testing,
|
|
129
122
|
// The overloads above carry the precise types; this body serves both.
|
|
@@ -220,7 +213,6 @@ export const mounts = (
|
|
|
220
213
|
name,
|
|
221
214
|
definition: installed.definition,
|
|
222
215
|
httpPrefix: installed.httpPrefix,
|
|
223
|
-
api: installed.api,
|
|
224
216
|
env: installed.env,
|
|
225
217
|
testing: installed.testing,
|
|
226
218
|
}));
|
package/src/Http.ts
CHANGED
|
@@ -70,10 +70,7 @@ export const make = <
|
|
|
70
70
|
export const isHttp = (u: unknown): u is Any =>
|
|
71
71
|
Predicate.hasProperty(u, TypeId) && u[TypeId] === TypeId;
|
|
72
72
|
|
|
73
|
-
export type Owner =
|
|
74
|
-
| { readonly kind: "app" }
|
|
75
|
-
| { readonly kind: "docs" }
|
|
76
|
-
| { readonly kind: "mount"; readonly mount: string };
|
|
73
|
+
export type Owner = { readonly kind: "app" } | { readonly kind: "docs" };
|
|
77
74
|
|
|
78
75
|
export interface Route {
|
|
79
76
|
readonly method: string;
|
|
@@ -101,7 +98,7 @@ export class DuplicateRoute extends Data.TaggedError("DuplicateRoute")<{
|
|
|
101
98
|
readonly second: Owner;
|
|
102
99
|
}> {
|
|
103
100
|
override get message(): string {
|
|
104
|
-
return `${this.method} ${this.path} is declared twice (${
|
|
101
|
+
return `${this.method} ${this.path} is declared twice (${this.first.kind} and ${this.second.kind}).`;
|
|
105
102
|
}
|
|
106
103
|
}
|
|
107
104
|
|
|
@@ -121,9 +118,6 @@ export type RouteError =
|
|
|
121
118
|
| DuplicateRoute
|
|
122
119
|
| OverlappingMountPrefixes;
|
|
123
120
|
|
|
124
|
-
const ownerLabel = (owner: Owner): string =>
|
|
125
|
-
owner.kind === "mount" ? `mount "${owner.mount}"` : owner.kind;
|
|
126
|
-
|
|
127
121
|
const trimSlash = (prefix: string): string =>
|
|
128
122
|
prefix.length > 1 && prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
|
|
129
123
|
|
|
@@ -157,14 +151,10 @@ interface KeyedRoute {
|
|
|
157
151
|
readonly key: string;
|
|
158
152
|
}
|
|
159
153
|
|
|
160
|
-
const apiRoutes = (
|
|
161
|
-
api: App.Api,
|
|
162
|
-
prefix: string,
|
|
163
|
-
owner: Owner,
|
|
164
|
-
): ReadonlyArray<KeyedRoute> =>
|
|
154
|
+
const apiRoutes = (api: App.Api, owner: Owner): ReadonlyArray<KeyedRoute> =>
|
|
165
155
|
Object.values<HttpApiGroup.Top>(api.groups).flatMap((group) =>
|
|
166
156
|
Object.values(group.endpoints).map((endpoint) => {
|
|
167
|
-
const path =
|
|
157
|
+
const path = endpoint.path;
|
|
168
158
|
const declared = paramNames(endpoint.params);
|
|
169
159
|
return {
|
|
170
160
|
route: { method: endpoint.method, path, owner },
|
|
@@ -174,20 +164,19 @@ const apiRoutes = (
|
|
|
174
164
|
);
|
|
175
165
|
|
|
176
166
|
/**
|
|
177
|
-
*
|
|
178
|
-
* mount's
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
* when two mount prefixes overlap.
|
|
167
|
+
* The backend's own HTTP routes and docs page. Fails when a route is at or
|
|
168
|
+
* under a mount's prefix (Convex accepts that and lets the app route silently
|
|
169
|
+
* shadow the component's), when two routes share a method and path, or when two
|
|
170
|
+
* mount prefixes overlap.
|
|
182
171
|
*/
|
|
183
172
|
export const routes = (
|
|
184
173
|
app: App.Any,
|
|
185
174
|
http?: Any,
|
|
186
175
|
): Result.Result<ReadonlyArray<Route>, RouteError> => {
|
|
187
|
-
const mounts = App.mounts(app).flatMap(({ name, httpPrefix
|
|
176
|
+
const mounts = App.mounts(app).flatMap(({ name, httpPrefix }) =>
|
|
188
177
|
httpPrefix === undefined
|
|
189
178
|
? []
|
|
190
|
-
: [{ name, httpPrefix: trimSlash(httpPrefix)
|
|
179
|
+
: [{ name, httpPrefix: trimSlash(httpPrefix) }],
|
|
191
180
|
);
|
|
192
181
|
|
|
193
182
|
for (const [index, first] of mounts.entries()) {
|
|
@@ -210,7 +199,7 @@ export const routes = (
|
|
|
210
199
|
http === undefined
|
|
211
200
|
? []
|
|
212
201
|
: [
|
|
213
|
-
...apiRoutes(http.api,
|
|
202
|
+
...apiRoutes(http.api, { kind: "app" }),
|
|
214
203
|
...(http.docs === undefined
|
|
215
204
|
? []
|
|
216
205
|
: [
|
|
@@ -241,16 +230,8 @@ export const routes = (
|
|
|
241
230
|
}
|
|
242
231
|
}
|
|
243
232
|
|
|
244
|
-
const all = [
|
|
245
|
-
...own,
|
|
246
|
-
...mounts.flatMap(({ name, httpPrefix, api }) =>
|
|
247
|
-
api === undefined
|
|
248
|
-
? []
|
|
249
|
-
: apiRoutes(api, httpPrefix, { kind: "mount", mount: name }),
|
|
250
|
-
),
|
|
251
|
-
];
|
|
252
233
|
const seen = new Map<string, Route>();
|
|
253
|
-
for (const { route, key } of
|
|
234
|
+
for (const { route, key } of own) {
|
|
254
235
|
const previous = seen.get(key);
|
|
255
236
|
if (previous !== undefined) {
|
|
256
237
|
return Result.fail(
|
|
@@ -264,5 +245,5 @@ export const routes = (
|
|
|
264
245
|
}
|
|
265
246
|
seen.set(key, route);
|
|
266
247
|
}
|
|
267
|
-
return Result.succeed(
|
|
248
|
+
return Result.succeed(own.map(({ route }) => route));
|
|
268
249
|
};
|