@farm.js/convex 0.0.0-stage → 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +63 -2
  3. package/dist/ConvexClientProvider.d.ts +3 -0
  4. package/dist/ConvexClientProvider.d.ts.map +1 -0
  5. package/dist/ConvexClientProvider.js +6 -0
  6. package/dist/adapter-client.d.ts +3 -0
  7. package/dist/adapter-client.d.ts.map +1 -0
  8. package/dist/adapter-client.js +21 -0
  9. package/dist/client.d.ts +5 -0
  10. package/dist/client.d.ts.map +1 -0
  11. package/dist/client.js +3 -0
  12. package/dist/clients.d.ts +3 -0
  13. package/dist/clients.d.ts.map +1 -0
  14. package/dist/clients.js +10 -0
  15. package/dist/component/_generated/api.d.ts +38 -0
  16. package/dist/component/_generated/api.d.ts.map +1 -0
  17. package/dist/component/_generated/api.js +30 -0
  18. package/dist/component/_generated/component.d.ts +57 -0
  19. package/dist/component/_generated/component.d.ts.map +1 -0
  20. package/dist/component/_generated/component.js +10 -0
  21. package/dist/component/_generated/dataModel.d.ts +46 -0
  22. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  23. package/dist/component/_generated/dataModel.js +10 -0
  24. package/dist/component/_generated/server.d.ts +133 -0
  25. package/dist/component/_generated/server.d.ts.map +1 -0
  26. package/dist/component/_generated/server.js +78 -0
  27. package/dist/component/convex.config.d.ts +3 -0
  28. package/dist/component/convex.config.d.ts.map +1 -0
  29. package/dist/component/convex.config.js +2 -0
  30. package/dist/component/rateLimits.d.ts +15 -0
  31. package/dist/component/rateLimits.d.ts.map +1 -0
  32. package/dist/component/rateLimits.js +40 -0
  33. package/dist/component/schema.d.ts +24 -0
  34. package/dist/component/schema.d.ts.map +1 -0
  35. package/dist/component/schema.js +13 -0
  36. package/dist/component/storage.d.ts +17 -0
  37. package/dist/component/storage.d.ts.map +1 -0
  38. package/dist/component/storage.js +62 -0
  39. package/dist/component/validators.d.ts +8 -0
  40. package/dist/component/validators.d.ts.map +1 -0
  41. package/dist/component/validators.js +5 -0
  42. package/dist/component.d.ts +12 -0
  43. package/dist/component.d.ts.map +1 -0
  44. package/dist/component.js +52 -0
  45. package/dist/detect.d.ts +6 -0
  46. package/dist/detect.d.ts.map +1 -0
  47. package/dist/detect.js +18 -0
  48. package/dist/dev.d.ts +10 -0
  49. package/dist/dev.d.ts.map +1 -0
  50. package/dist/dev.js +122 -0
  51. package/dist/environment.d.ts +6 -0
  52. package/dist/environment.d.ts.map +1 -0
  53. package/dist/environment.js +62 -0
  54. package/dist/extensions.d.ts +8 -0
  55. package/dist/extensions.d.ts.map +1 -0
  56. package/dist/extensions.js +55 -0
  57. package/dist/forward.d.ts +2 -0
  58. package/dist/forward.d.ts.map +1 -0
  59. package/dist/forward.js +38 -0
  60. package/dist/functions.d.ts +42 -0
  61. package/dist/functions.d.ts.map +1 -0
  62. package/dist/functions.js +90 -0
  63. package/dist/homes.d.ts +11 -0
  64. package/dist/homes.d.ts.map +1 -0
  65. package/dist/homes.js +81 -0
  66. package/dist/index.d.ts +35 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +87 -0
  69. package/dist/known.d.ts +7 -0
  70. package/dist/known.d.ts.map +1 -0
  71. package/dist/known.js +33 -0
  72. package/dist/pairs.d.ts +6 -0
  73. package/dist/pairs.d.ts.map +1 -0
  74. package/dist/pairs.js +28 -0
  75. package/dist/rate-limit.d.ts +4 -0
  76. package/dist/rate-limit.d.ts.map +1 -0
  77. package/dist/rate-limit.js +16 -0
  78. package/dist/runtime.d.ts +5 -0
  79. package/dist/runtime.d.ts.map +1 -0
  80. package/dist/runtime.js +14 -0
  81. package/dist/secret.d.ts +3 -0
  82. package/dist/secret.d.ts.map +1 -0
  83. package/dist/secret.js +8 -0
  84. package/dist/server.d.ts +10 -0
  85. package/dist/server.d.ts.map +1 -0
  86. package/dist/server.js +70 -0
  87. package/dist/site-token.d.ts +4 -0
  88. package/dist/site-token.d.ts.map +1 -0
  89. package/dist/site-token.js +26 -0
  90. package/dist/storage.d.ts +6 -0
  91. package/dist/storage.d.ts.map +1 -0
  92. package/dist/storage.js +49 -0
  93. package/dist/token-auth.d.ts +13 -0
  94. package/dist/token-auth.d.ts.map +1 -0
  95. package/dist/token-auth.js +69 -0
  96. package/dist/types.d.ts +165 -0
  97. package/dist/types.d.ts.map +1 -0
  98. package/dist/types.js +1 -0
  99. package/package.json +76 -3
@@ -0,0 +1,42 @@
1
+ import type { ComponentApi } from "./component/_generated/component.js";
2
+ export declare function requireAdapterSecret(secret: string): void;
3
+ export declare function defineFarmFunctions(component: ComponentApi): {
4
+ setStorageItem: import("convex/server").RegisteredMutation<"public", {
5
+ key: string;
6
+ secret: string;
7
+ value: string;
8
+ }, Promise<null>>;
9
+ removeStorageItem: import("convex/server").RegisteredMutation<"public", {
10
+ key: string;
11
+ secret: string;
12
+ }, Promise<null>>;
13
+ clearStorageItems: import("convex/server").RegisteredMutation<"public", {
14
+ secret: string;
15
+ prefix: string;
16
+ }, Promise<number>>;
17
+ getStorageItem: import("convex/server").RegisteredQuery<"public", {
18
+ key: string;
19
+ secret: string;
20
+ }, Promise<string | null>>;
21
+ listStorageKeys: import("convex/server").RegisteredQuery<"public", {
22
+ secret: string;
23
+ prefix: string;
24
+ }, Promise<string[]>>;
25
+ incrementRateLimit: import("convex/server").RegisteredMutation<"public", {
26
+ key: string;
27
+ secret: string;
28
+ windowMs: number;
29
+ }, Promise<{
30
+ count: number;
31
+ resetAt: number;
32
+ }>>;
33
+ readRateLimit: import("convex/server").RegisteredQuery<"public", {
34
+ key: string;
35
+ secret: string;
36
+ now: number;
37
+ }, Promise<{
38
+ count: number;
39
+ resetAt: number;
40
+ } | null>>;
41
+ };
42
+ //# sourceMappingURL=functions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"functions.d.ts","sourceRoot":"","sources":["../src/functions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAaxE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAQzD;AAkED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAM1D"}
@@ -0,0 +1,90 @@
1
+ import { mutationGeneric, queryGeneric } from "convex/server";
2
+ import { v } from "convex/values";
3
+ import { SECRET_ENVIRONMENT_NAME } from "./secret.js";
4
+ function areStringsEqual(left, right) {
5
+ const encoder = new TextEncoder();
6
+ const leftBytes = encoder.encode(left);
7
+ const rightBytes = encoder.encode(right);
8
+ let difference = leftBytes.length ^ rightBytes.length;
9
+ for (let index = 0; index < leftBytes.length; index += 1) {
10
+ difference |= leftBytes[index] ^ (rightBytes[index % rightBytes.length] ?? 0);
11
+ }
12
+ return difference === 0;
13
+ }
14
+ export function requireAdapterSecret(secret) {
15
+ const expected = process.env[SECRET_ENVIRONMENT_NAME];
16
+ if (!expected) {
17
+ throw new Error(`${SECRET_ENVIRONMENT_NAME} is not set on this Convex deployment.`);
18
+ }
19
+ if (!areStringsEqual(secret, expected)) {
20
+ throw new Error(`The ${SECRET_ENVIRONMENT_NAME} sent by Farm does not match this deployment.`);
21
+ }
22
+ }
23
+ function defineRateLimitFunctions(component) {
24
+ return {
25
+ incrementRateLimit: mutationGeneric({
26
+ args: { secret: v.string(), key: v.string(), windowMs: v.number() },
27
+ handler: (context, { secret, ...args }) => {
28
+ requireAdapterSecret(secret);
29
+ return context.runMutation(component.rateLimits.increment, args);
30
+ },
31
+ }),
32
+ readRateLimit: queryGeneric({
33
+ args: { secret: v.string(), key: v.string(), now: v.number() },
34
+ handler: (context, { secret, ...args }) => {
35
+ requireAdapterSecret(secret);
36
+ return context.runQuery(component.rateLimits.read, args);
37
+ },
38
+ }),
39
+ };
40
+ }
41
+ function defineStorageReadFunctions(component) {
42
+ return {
43
+ getStorageItem: queryGeneric({
44
+ args: { secret: v.string(), key: v.string() },
45
+ handler: (context, { secret, ...args }) => {
46
+ requireAdapterSecret(secret);
47
+ return context.runQuery(component.storage.getItem, args);
48
+ },
49
+ }),
50
+ listStorageKeys: queryGeneric({
51
+ args: { secret: v.string(), prefix: v.string() },
52
+ handler: (context, { secret, ...args }) => {
53
+ requireAdapterSecret(secret);
54
+ return context.runQuery(component.storage.listKeys, args);
55
+ },
56
+ }),
57
+ };
58
+ }
59
+ function defineStorageWriteFunctions(component) {
60
+ return {
61
+ setStorageItem: mutationGeneric({
62
+ args: { secret: v.string(), key: v.string(), value: v.string() },
63
+ handler: (context, { secret, ...args }) => {
64
+ requireAdapterSecret(secret);
65
+ return context.runMutation(component.storage.setItem, args);
66
+ },
67
+ }),
68
+ removeStorageItem: mutationGeneric({
69
+ args: { secret: v.string(), key: v.string() },
70
+ handler: (context, { secret, ...args }) => {
71
+ requireAdapterSecret(secret);
72
+ return context.runMutation(component.storage.removeItem, args);
73
+ },
74
+ }),
75
+ clearStorageItems: mutationGeneric({
76
+ args: { secret: v.string(), prefix: v.string() },
77
+ handler: (context, { secret, ...args }) => {
78
+ requireAdapterSecret(secret);
79
+ return context.runMutation(component.storage.clearItems, args);
80
+ },
81
+ }),
82
+ };
83
+ }
84
+ export function defineFarmFunctions(component) {
85
+ return {
86
+ ...defineRateLimitFunctions(component),
87
+ ...defineStorageReadFunctions(component),
88
+ ...defineStorageWriteFunctions(component),
89
+ };
90
+ }
@@ -0,0 +1,11 @@
1
+ import type { ConvexExtension, ConvexFarmConfigView, ConvexHomeConflict } from "./types.js";
2
+ import type { ConvexHomes, ConvexServiceSetup } from "./types.js";
3
+ export declare function readConvexConfigSource(root: string, functionsDirectory: string): string | undefined;
4
+ export declare function findComponentPackages(source: string): string[];
5
+ export declare function findConvexComponents(source: string): ConvexServiceSetup[];
6
+ export declare function findExtensionServices(extensions: readonly ConvexExtension[]): ConvexServiceSetup[];
7
+ export declare function findFarmIntegrations(config: ConvexFarmConfigView): ConvexServiceSetup[];
8
+ export declare function findHomeConflicts(components: readonly ConvexServiceSetup[], integrations: readonly ConvexServiceSetup[], homes: ConvexHomes): ConvexHomeConflict[];
9
+ export declare function describeHomeConflict(conflict: ConvexHomeConflict): string;
10
+ export declare function checkServiceHomes(config: ConvexFarmConfigView, functionsDirectory: string, homes: ConvexHomes, extensions?: readonly ConvexExtension[]): void;
11
+ //# sourceMappingURL=homes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homes.d.ts","sourceRoot":"","sources":["../src/homes.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC5F,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAMlE,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,kBAAkB,EAAE,MAAM,GACzB,MAAM,GAAG,SAAS,CAKpB;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9D;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAKzE;AAGD,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,SAAS,eAAe,EAAE,GACrC,kBAAkB,EAAE,CAMtB;AA0BD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,kBAAkB,EAAE,CASvF;AAWD,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,SAAS,kBAAkB,EAAE,EACzC,YAAY,EAAE,SAAS,kBAAkB,EAAE,EAC3C,KAAK,EAAE,WAAW,GACjB,kBAAkB,EAAE,CAatB;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,MAAM,CAEzE;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,oBAAoB,EAC5B,kBAAkB,EAAE,MAAM,EAC1B,KAAK,EAAE,WAAW,EAClB,UAAU,GAAE,SAAS,eAAe,EAAO,GAC1C,IAAI,CAUN"}
package/dist/homes.js ADDED
@@ -0,0 +1,81 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { readExtensionService } from "./component.js";
4
+ import { CONVEX_COMPONENT_SERVICES, CONVEX_PAIRS } from "./pairs.js";
5
+ import { FARM_BUILT_IN_AUTH, FARM_INTEGRATION_SERVICES } from "./pairs.js";
6
+ const COMPONENT_IMPORT_PATTERN = /from\s+["']((?:@[\w.-]+\/)?[\w.-]+)\/convex\.config(?:\.js)?["']/g;
7
+ const CONFIG_FILE_NAMES = ["convex.config.ts", "convex.config.js"];
8
+ export function readConvexConfigSource(root, functionsDirectory) {
9
+ const filePath = CONFIG_FILE_NAMES.map((name) => path.join(root, functionsDirectory, name)).find((candidate) => existsSync(candidate));
10
+ return filePath ? readFileSync(filePath, "utf8") : undefined;
11
+ }
12
+ export function findComponentPackages(source) {
13
+ return [...source.matchAll(COMPONENT_IMPORT_PATTERN)].map((match) => match[1]);
14
+ }
15
+ export function findConvexComponents(source) {
16
+ return [...source.matchAll(COMPONENT_IMPORT_PATTERN)]
17
+ .map((match) => match[1])
18
+ .filter((name) => CONVEX_COMPONENT_SERVICES[name] !== undefined)
19
+ .map((name) => ({ service: CONVEX_COMPONENT_SERVICES[name], side: "convex", name }));
20
+ }
21
+ // An extension's service counts even before its component is installed.
22
+ export function findExtensionServices(extensions) {
23
+ return extensions.flatMap((extension) => {
24
+ const service = readExtensionService(extension);
25
+ const name = extension.component ?? extension.name;
26
+ return service ? [{ service, side: "convex", name }] : [];
27
+ });
28
+ }
29
+ function mergeConvexServices(components, extensions) {
30
+ const names = new Set(components.map((setup) => setup.name));
31
+ return [...components, ...extensions.filter((setup) => !names.has(setup.name))];
32
+ }
33
+ function warnMissingComponents(source, extensions) {
34
+ for (const { name, component } of extensions) {
35
+ if (!component || source.includes(`${component}/convex.config`))
36
+ continue;
37
+ const listed = name === component ? component : `${name} (${component})`;
38
+ console.warn(`[farm:convex] ${listed} is in convex({ use }) but not in convex.config.ts. Add app.use() for it there.`);
39
+ }
40
+ }
41
+ function isBuiltInAuthEnabled(auth) {
42
+ if (typeof auth === "boolean")
43
+ return auth;
44
+ if (typeof auth !== "object" || auth === null)
45
+ return false;
46
+ return Reflect.get(auth, "enabled") !== false;
47
+ }
48
+ export function findFarmIntegrations(config) {
49
+ const names = Object.values(config.integrations ?? {})
50
+ .flatMap((integration) => (integration ? [integration.type] : []))
51
+ .concat(isBuiltInAuthEnabled(config.auth) ? [FARM_BUILT_IN_AUTH] : []);
52
+ return names
53
+ .filter((name) => name !== undefined && FARM_INTEGRATION_SERVICES[name] !== undefined)
54
+ .map((name) => ({ service: FARM_INTEGRATION_SERVICES[name], side: "farm", name }));
55
+ }
56
+ function isTogether(component, integration) {
57
+ return CONVEX_PAIRS.some((pair) => pair.component === component &&
58
+ pair.integration === integration &&
59
+ pair.relation === "together");
60
+ }
61
+ export function findHomeConflicts(components, integrations, homes) {
62
+ return components.flatMap((component) => integrations
63
+ .filter((integration) => integration.service === component.service && !homes[component.service])
64
+ .filter((integration) => !isTogether(component.name, integration.name))
65
+ .map((integration) => ({
66
+ service: component.service,
67
+ component: component.name,
68
+ integration: integration.name,
69
+ })));
70
+ }
71
+ export function describeHomeConflict(conflict) {
72
+ return `${conflict.service} is set up in Convex (${conflict.component}) and in Farm (${conflict.integration}). Remove one, or set convex({ home: { ${conflict.service}: "convex" } }) or "farm" to say which side owns it.`;
73
+ }
74
+ export function checkServiceHomes(config, functionsDirectory, homes, extensions = []) {
75
+ const source = readConvexConfigSource(config.root ?? process.cwd(), functionsDirectory) ?? "";
76
+ warnMissingComponents(source, extensions);
77
+ const conflicts = findHomeConflicts(mergeConvexServices(findConvexComponents(source), findExtensionServices(extensions)), findFarmIntegrations(config), homes);
78
+ if (conflicts.length === 0)
79
+ return;
80
+ throw new Error(`Convex integration:\n${conflicts.map(describeHomeConflict).join("\n")}`);
81
+ }
@@ -0,0 +1,35 @@
1
+ import type { ConvexIntegrationInput } from "./types.js";
2
+ export declare function convex(input?: ConvexIntegrationInput): import("@farm.js/core").DefinedIntegration<{
3
+ category: "database";
4
+ type: string;
5
+ instance: undefined;
6
+ log: import("@farm.js/core").FarmIntegrationLogger | undefined;
7
+ plugins: import("@farm.js/core").FarmPlugin<import("./types.js").ConvexDevState, Record<string, never>, unknown, undefined, unknown, false, import("@farm.js/core").PluginRoutes>[];
8
+ }, import("@farm.js/core").FarmSchema | undefined> | import("@farm.js/core").DefinedIntegration<{
9
+ category: "database";
10
+ type: string;
11
+ instance: import("./types.js").ConvexDeploymentURLs;
12
+ log: import("@farm.js/core").FarmIntegrationLogger | undefined;
13
+ routes: import("@farm.js/core").FarmIntegrationRoute<unknown, unknown, import("@farm.js/core").FarmSchema | undefined>[];
14
+ providers: {
15
+ name: string;
16
+ type: string;
17
+ props: {
18
+ url: string;
19
+ tokenPath: string;
20
+ } | {
21
+ url: string;
22
+ tokenPath?: undefined;
23
+ };
24
+ supportsIsolatedHydration: true;
25
+ component: import("@farm.js/core").FarmIntegrationProviderComponentReference;
26
+ }[];
27
+ plugins: import("@farm.js/core").FarmPlugin<import("./types.js").ConvexDevState, Record<string, never>, unknown, undefined, unknown, false, import("@farm.js/core").PluginRoutes>[];
28
+ }, import("@farm.js/core").FarmSchema | undefined>;
29
+ export { component } from "./component.js";
30
+ export { KNOWN_COMPONENTS } from "./known.js";
31
+ export { convexRateLimitStorage } from "./rate-limit.js";
32
+ export { convexStorage } from "./storage.js";
33
+ export { CONVEX_PAIRS } from "./pairs.js";
34
+ export type { ConvexAdapterOptions, ConvexComponentOptions, ConvexDevOptions, ConvexExtension, ConvexExtensionRoute, ConvexHomes, ConvexIntegrationInput, ConvexPair, ConvexService, ConvexServiceSide, ConvexSiteToken, ConvexTokenReader, } from "./types.js";
35
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAmB,sBAAsB,EAAqB,MAAM,YAAY,CAAC;AAkD7F,wBAAgB,MAAM,CAAC,KAAK,GAAE,sBAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;mDAkCxD;AAED,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,YAAY,EACV,oBAAoB,EACpB,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,WAAW,EACX,sBAAsB,EACtB,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,87 @@
1
+ import { defineIntegration } from "@farm.js/core";
2
+ import { createConvexPlugin, resolveDevOptions } from "./dev.js";
3
+ import { detectExtensions, mergeExtensions } from "./detect.js";
4
+ import { hasDeploymentURL, resolveDeploymentURLs } from "./environment.js";
5
+ import { checkExtensions, createExtensionRoutes, pickExtensionValue } from "./extensions.js";
6
+ import { setConvexRuntime } from "./runtime.js";
7
+ import { createSiteTokenReader } from "./site-token.js";
8
+ const DEFAULT_PROVIDER = { module: "@farm.js/convex/client", export: "ConvexClientProvider" };
9
+ const DEFAULT_FUNCTIONS_DIRECTORY = "convex";
10
+ function resolveTokenReader(input, extensions, siteURL) {
11
+ if (input.token)
12
+ return input.token;
13
+ const token = pickExtensionValue(extensions, "token");
14
+ if (token)
15
+ return token;
16
+ const siteToken = pickExtensionValue(extensions, "siteToken");
17
+ return siteToken ? createSiteTokenReader(siteToken, siteURL) : undefined;
18
+ }
19
+ function isDevelopment() {
20
+ const mode = process.env.NODE_ENV;
21
+ return mode === undefined || mode === "development";
22
+ }
23
+ function resolveExtensions(input, functionsDirectory) {
24
+ const detected = input.detect === false
25
+ ? []
26
+ : detectExtensions(process.cwd(), functionsDirectory, input.home ?? {});
27
+ return mergeExtensions(detected, input.use ?? []);
28
+ }
29
+ // A new app has no deployment yet. farm dev runs convex dev, which creates one and writes
30
+ // CONVEX_URL to .env.local; the app connects after a restart.
31
+ function waitForDeployment(input, functionsDirectory) {
32
+ return defineIntegration({
33
+ category: "database",
34
+ type: "convex",
35
+ instance: undefined,
36
+ log: input.log,
37
+ plugins: [
38
+ createConvexPlugin({
39
+ dev: resolveDevOptions(input.dev),
40
+ homes: input.home ?? {},
41
+ functionsDirectory,
42
+ extensions: [],
43
+ waitingForDeployment: true,
44
+ }),
45
+ ],
46
+ });
47
+ }
48
+ export function convex(input = {}) {
49
+ const functionsDirectory = input.functionsDirectory ?? DEFAULT_FUNCTIONS_DIRECTORY;
50
+ if (!hasDeploymentURL(input) && isDevelopment()) {
51
+ return waitForDeployment(input, functionsDirectory);
52
+ }
53
+ const urls = resolveDeploymentURLs(input);
54
+ const extensions = resolveExtensions(input, functionsDirectory);
55
+ checkExtensions(extensions);
56
+ setConvexRuntime({ ...urls, token: resolveTokenReader(input, extensions, urls.siteURL) });
57
+ const tokenPath = input.tokenPath ?? pickExtensionValue(extensions, "tokenPath");
58
+ return defineIntegration({
59
+ category: "database",
60
+ type: "convex",
61
+ instance: urls,
62
+ log: input.log,
63
+ routes: createExtensionRoutes(extensions, urls.siteURL),
64
+ providers: [
65
+ {
66
+ name: "convex-provider",
67
+ type: "convex",
68
+ props: tokenPath ? { url: urls.url, tokenPath } : { url: urls.url },
69
+ supportsIsolatedHydration: true,
70
+ component: input.provider ?? pickExtensionValue(extensions, "provider") ?? DEFAULT_PROVIDER,
71
+ },
72
+ ],
73
+ plugins: [
74
+ createConvexPlugin({
75
+ dev: resolveDevOptions(input.dev),
76
+ homes: input.home ?? {},
77
+ functionsDirectory,
78
+ extensions,
79
+ }),
80
+ ],
81
+ });
82
+ }
83
+ export { component } from "./component.js";
84
+ export { KNOWN_COMPONENTS } from "./known.js";
85
+ export { convexRateLimitStorage } from "./rate-limit.js";
86
+ export { convexStorage } from "./storage.js";
87
+ export { CONVEX_PAIRS } from "./pairs.js";
@@ -0,0 +1,7 @@
1
+ import type { ConvexKnownComponent } from "./types.js";
2
+ /**
3
+ * Defaults for well-known Convex components, as data: Farm needs no code or dependencies from them.
4
+ * Paths match what each component registers by default in convex/http.ts.
5
+ */
6
+ export declare const KNOWN_COMPONENTS: Readonly<Record<string, ConvexKnownComponent>>;
7
+ //# sourceMappingURL=known.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"known.d.ts","sourceRoot":"","sources":["../src/known.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CA4B3E,CAAC"}
package/dist/known.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Defaults for well-known Convex components, as data: Farm needs no code or dependencies from them.
3
+ * Paths match what each component registers by default in convex/http.ts.
4
+ */
5
+ export const KNOWN_COMPONENTS = {
6
+ "@convex-dev/better-auth": {
7
+ name: "better-auth",
8
+ service: "auth",
9
+ routes: [{ path: "/api/auth/[...auth]" }],
10
+ siteToken: { path: "/api/auth/convex/token", cookie: "session_token=" },
11
+ tokenPath: "/api/auth/convex/token",
12
+ },
13
+ "@convex-dev/workos-authkit": {
14
+ name: "workos-authkit",
15
+ service: "auth",
16
+ webhook: { path: "/api/webhooks/workos", convexPath: "/workos/webhook" },
17
+ },
18
+ "@convex-dev/polar": {
19
+ name: "polar",
20
+ service: "billing",
21
+ webhook: { path: "/api/webhooks/polar", convexPath: "/polar/events" },
22
+ },
23
+ "@convex-dev/stripe": {
24
+ name: "stripe",
25
+ service: "billing",
26
+ webhook: { path: "/api/webhooks/stripe", convexPath: "/stripe/webhook" },
27
+ },
28
+ "@useautumn/convex": { name: "autumn", service: "billing" },
29
+ "@convex-dev/resend": {
30
+ name: "resend",
31
+ webhook: { path: "/api/webhooks/resend", convexPath: "/resend-webhook" },
32
+ },
33
+ };
@@ -0,0 +1,6 @@
1
+ import type { ConvexPair, ConvexService } from "./types.js";
2
+ export declare const FARM_BUILT_IN_AUTH = "farm-auth";
3
+ export declare const CONVEX_COMPONENT_SERVICES: Readonly<Record<string, ConvexService>>;
4
+ export declare const FARM_INTEGRATION_SERVICES: Readonly<Record<string, ConvexService>>;
5
+ export declare const CONVEX_PAIRS: readonly ConvexPair[];
6
+ //# sourceMappingURL=pairs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairs.d.ts","sourceRoot":"","sources":["../src/pairs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAE9C,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAM7E,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAW7E,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAO7C,CAAC"}
package/dist/pairs.js ADDED
@@ -0,0 +1,28 @@
1
+ export const FARM_BUILT_IN_AUTH = "farm-auth";
2
+ export const CONVEX_COMPONENT_SERVICES = {
3
+ "@convex-dev/better-auth": "auth",
4
+ "@convex-dev/workos-authkit": "auth",
5
+ "@convex-dev/polar": "billing",
6
+ "@convex-dev/stripe": "billing",
7
+ "@useautumn/convex": "billing",
8
+ };
9
+ export const FARM_INTEGRATION_SERVICES = {
10
+ [FARM_BUILT_IN_AUTH]: "auth",
11
+ "better-auth": "auth",
12
+ clerk: "auth",
13
+ workos: "auth",
14
+ auth0: "auth",
15
+ authjs: "auth",
16
+ supabase: "auth",
17
+ polar: "billing",
18
+ stripe: "billing",
19
+ autumn: "billing",
20
+ };
21
+ export const CONVEX_PAIRS = [
22
+ {
23
+ service: "auth",
24
+ component: "@convex-dev/workos-authkit",
25
+ integration: "workos",
26
+ relation: "together",
27
+ },
28
+ ];
@@ -0,0 +1,4 @@
1
+ import type { RateLimitStorage } from "@farm.js/core/middleware";
2
+ import type { ConvexAdapterOptions } from "./types.js";
3
+ export declare function convexRateLimitStorage(options?: ConvexAdapterOptions): RateLimitStorage;
4
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAA4B,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAQvD,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,oBAAyB,GAAG,gBAAgB,CAU3F"}
@@ -0,0 +1,16 @@
1
+ import { createAdapterCaller } from "./adapter-client.js";
2
+ function checkWindow(windowMs) {
3
+ if (!Number.isSafeInteger(windowMs) || windowMs <= 0) {
4
+ throw new TypeError("Rate-limit windowMs must be a positive safe integer.");
5
+ }
6
+ }
7
+ export function convexRateLimitStorage(options = {}) {
8
+ const caller = createAdapterCaller(options);
9
+ return {
10
+ increment(key, windowMs) {
11
+ checkWindow(windowMs);
12
+ return caller.mutation("incrementRateLimit", { key, windowMs });
13
+ },
14
+ get: (key) => caller.query("readRateLimit", { key, now: Date.now() }),
15
+ };
16
+ }
@@ -0,0 +1,5 @@
1
+ import type { ConvexRuntime } from "./types.js";
2
+ export declare function setConvexRuntime(runtime: ConvexRuntime): void;
3
+ export declare function readConvexRuntime(): ConvexRuntime | undefined;
4
+ export declare function requireConvexRuntime(): ConvexRuntime;
5
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAuB,MAAM,YAAY,CAAC;AAIrE,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAE7D;AAED,wBAAgB,iBAAiB,IAAI,aAAa,GAAG,SAAS,CAE7D;AAED,wBAAgB,oBAAoB,IAAI,aAAa,CAQpD"}
@@ -0,0 +1,14 @@
1
+ const runtimeHolder = globalThis;
2
+ export function setConvexRuntime(runtime) {
3
+ runtimeHolder.__farmConvexRuntime = runtime;
4
+ }
5
+ export function readConvexRuntime() {
6
+ return runtimeHolder.__farmConvexRuntime;
7
+ }
8
+ export function requireConvexRuntime() {
9
+ const runtime = readConvexRuntime();
10
+ if (!runtime) {
11
+ throw new Error("@farm.js/convex/server needs convex() in the farm.config.ts integrations with CONVEX_URL set. On a new app, let farm dev create the deployment, then restart it.");
12
+ }
13
+ return runtime;
14
+ }
@@ -0,0 +1,3 @@
1
+ export declare const SECRET_ENVIRONMENT_NAME = "FARM_CONVEX_SECRET";
2
+ export declare function readAdapterSecret(secret: string | undefined): string;
3
+ //# sourceMappingURL=secret.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret.d.ts","sourceRoot":"","sources":["../src/secret.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,uBAAuB,CAAC;AAE5D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAQpE"}
package/dist/secret.js ADDED
@@ -0,0 +1,8 @@
1
+ export const SECRET_ENVIRONMENT_NAME = "FARM_CONVEX_SECRET";
2
+ export function readAdapterSecret(secret) {
3
+ const resolvedSecret = secret ?? process.env[SECRET_ENVIRONMENT_NAME];
4
+ if (!resolvedSecret) {
5
+ throw new Error(`Convex adapters need ${SECRET_ENVIRONMENT_NAME}. Set the same value in your Farm server env and with \`npx convex env set ${SECRET_ENVIRONMENT_NAME} <value>\`.`);
6
+ }
7
+ return resolvedSecret;
8
+ }
@@ -0,0 +1,10 @@
1
+ import { ConvexHttpClient } from "convex/browser";
2
+ import type { Preloaded } from "convex/react";
3
+ import type { ArgsAndOptions, FunctionReference, FunctionReturnType } from "convex/server";
4
+ import type { ConvexServerOptions } from "./types.js";
5
+ export declare function createConvexServerClient(options?: ConvexServerOptions): Promise<ConvexHttpClient>;
6
+ export declare function fetchQuery<Query extends FunctionReference<"query">>(query: Query, ...argsAndOptions: ArgsAndOptions<Query, ConvexServerOptions>): Promise<FunctionReturnType<Query>>;
7
+ export declare function fetchMutation<Mutation extends FunctionReference<"mutation">>(mutation: Mutation, ...argsAndOptions: ArgsAndOptions<Mutation, ConvexServerOptions>): Promise<FunctionReturnType<Mutation>>;
8
+ export declare function fetchAction<Action extends FunctionReference<"action">>(action: Action, ...argsAndOptions: ArgsAndOptions<Action, ConvexServerOptions>): Promise<FunctionReturnType<Action>>;
9
+ export declare function preloadQuery<Query extends FunctionReference<"query">>(query: Query, ...argsAndOptions: ArgsAndOptions<Query, ConvexServerOptions>): Promise<Preloaded<Query>>;
10
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAOlD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,KAAK,EAAiB,mBAAmB,EAAE,MAAM,YAAY,CAAC;AA4CrE,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,gBAAgB,CAAC,CAI3B;AAED,wBAAsB,UAAU,CAAC,KAAK,SAAS,iBAAiB,CAAC,OAAO,CAAC,EACvE,KAAK,EAAE,KAAK,EACZ,GAAG,cAAc,EAAE,cAAc,CAAC,KAAK,EAAE,mBAAmB,CAAC,GAC5D,OAAO,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAIpC;AAED,wBAAsB,aAAa,CAAC,QAAQ,SAAS,iBAAiB,CAAC,UAAU,CAAC,EAChF,QAAQ,EAAE,QAAQ,EAClB,GAAG,cAAc,EAAE,cAAc,CAAC,QAAQ,EAAE,mBAAmB,CAAC,GAC/D,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAIvC;AAED,wBAAsB,WAAW,CAAC,MAAM,SAAS,iBAAiB,CAAC,QAAQ,CAAC,EAC1E,MAAM,EAAE,MAAM,EACd,GAAG,cAAc,EAAE,cAAc,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAC7D,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAIrC;AAED,wBAAsB,YAAY,CAAC,KAAK,SAAS,iBAAiB,CAAC,OAAO,CAAC,EACzE,KAAK,EAAE,KAAK,EACZ,GAAG,cAAc,EAAE,cAAc,CAAC,KAAK,EAAE,mBAAmB,CAAC,GAC5D,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAS3B"}
package/dist/server.js ADDED
@@ -0,0 +1,70 @@
1
+ import { ConvexHttpClient } from "convex/browser";
2
+ import { getFunctionName } from "convex/server";
3
+ import { convexToJson } from "convex/values";
4
+ import { getCurrentRequest } from "@farm.js/core/request";
5
+ import { requireConvexRuntime } from "./runtime.js";
6
+ const MISSING_REQUEST_MESSAGE = "No current request is available";
7
+ function readCurrentRequest() {
8
+ try {
9
+ return getCurrentRequest();
10
+ }
11
+ catch (error) {
12
+ const message = error instanceof Error ? error.message : String(error);
13
+ if (message.startsWith(MISSING_REQUEST_MESSAGE))
14
+ return undefined;
15
+ throw error;
16
+ }
17
+ }
18
+ const fetchWithoutCache = (input, init) => fetch(input, { ...init, cache: "no-store" });
19
+ function keepPageOutOfSharedCache() {
20
+ readCurrentRequest();
21
+ }
22
+ // A page that runs several queries reads the visitor's token once.
23
+ const tokensByRequest = new WeakMap();
24
+ async function readRequestToken(reader, request) {
25
+ let token = tokensByRequest.get(request);
26
+ if (!token) {
27
+ token = Promise.resolve(reader(request)).then((value) => value ?? undefined);
28
+ tokensByRequest.set(request, token);
29
+ }
30
+ return token;
31
+ }
32
+ async function resolveToken(options) {
33
+ if (options.token !== undefined)
34
+ return options.token;
35
+ const runtime = requireConvexRuntime();
36
+ const request = options.request ?? readCurrentRequest();
37
+ if (!runtime.token || !request)
38
+ return undefined;
39
+ return readRequestToken(runtime.token, request);
40
+ }
41
+ export async function createConvexServerClient(options = {}) {
42
+ const runtime = requireConvexRuntime();
43
+ const token = await resolveToken(options);
44
+ return new ConvexHttpClient(runtime.url, { auth: token, fetch: fetchWithoutCache });
45
+ }
46
+ export async function fetchQuery(query, ...argsAndOptions) {
47
+ const [args, options] = argsAndOptions;
48
+ const client = await createConvexServerClient(options ?? {});
49
+ return client.query(query, args ?? {});
50
+ }
51
+ export async function fetchMutation(mutation, ...argsAndOptions) {
52
+ const [args, options] = argsAndOptions;
53
+ const client = await createConvexServerClient(options ?? {});
54
+ return client.mutation(mutation, args ?? {});
55
+ }
56
+ export async function fetchAction(action, ...argsAndOptions) {
57
+ const [args, options] = argsAndOptions;
58
+ const client = await createConvexServerClient(options ?? {});
59
+ return client.action(action, args ?? {});
60
+ }
61
+ export async function preloadQuery(query, ...argsAndOptions) {
62
+ const [args] = argsAndOptions;
63
+ keepPageOutOfSharedCache();
64
+ const value = await fetchQuery(query, ...argsAndOptions);
65
+ return {
66
+ _name: getFunctionName(query),
67
+ _argsJSON: convexToJson(args ?? {}),
68
+ _valueJSON: convexToJson(value),
69
+ };
70
+ }
@@ -0,0 +1,4 @@
1
+ import type { ConvexSiteToken, ConvexTokenReader } from "./types.js";
2
+ /** Exchange the visitor's cookie for a Convex token at a path on the Convex site URL. */
3
+ export declare function createSiteTokenReader(siteToken: ConvexSiteToken, siteURL: string): ConvexTokenReader;
4
+ //# sourceMappingURL=site-token.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"site-token.d.ts","sourceRoot":"","sources":["../src/site-token.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAgBrE,yFAAyF;AACzF,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,eAAe,EAC1B,OAAO,EAAE,MAAM,GACd,iBAAiB,CAWnB"}
@@ -0,0 +1,26 @@
1
+ function isTokenPayload(value) {
2
+ return (typeof value === "object" && value !== null && typeof Reflect.get(value, "token") === "string");
3
+ }
4
+ async function readTokenPayload(response) {
5
+ try {
6
+ return await response.json();
7
+ }
8
+ catch {
9
+ return undefined;
10
+ }
11
+ }
12
+ /** Exchange the visitor's cookie for a Convex token at a path on the Convex site URL. */
13
+ export function createSiteTokenReader(siteToken, siteURL) {
14
+ return async (request) => {
15
+ const cookie = request.headers.get("cookie");
16
+ if (!cookie || (siteToken.cookie && !cookie.includes(siteToken.cookie)))
17
+ return undefined;
18
+ const response = await fetch(new URL(siteToken.path, siteURL), {
19
+ headers: { cookie, "accept-encoding": "identity" },
20
+ });
21
+ if (!response.ok)
22
+ return undefined;
23
+ const payload = await readTokenPayload(response);
24
+ return isTokenPayload(payload) ? payload.token : undefined;
25
+ };
26
+ }