@confect/core 9.0.0-next.5 → 9.0.0-next.7

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/package.json CHANGED
@@ -1,25 +1,28 @@
1
1
  {
2
2
  "name": "@confect/core",
3
- "version": "9.0.0-next.5",
4
3
  "description": "Shared specs and schemas used by all Confect packages",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/rjdellecese/confect.git"
8
- },
4
+ "version": "9.0.0-next.7",
5
+ "author": "RJ Dellecese",
9
6
  "bugs": {
10
7
  "url": "https://github.com/rjdellecese/confect/issues"
11
8
  },
12
- "homepage": "https://confect.dev",
13
- "sideEffects": false,
14
- "type": "module",
15
- "files": [
16
- "CHANGELOG.md",
17
- "LICENSE",
18
- "README.md",
19
- "dist",
20
- "package.json",
21
- "src"
22
- ],
9
+ "devDependencies": {
10
+ "@ark/attest": "0.56.0",
11
+ "@effect/vitest": "0.29.0",
12
+ "@eslint/js": "10.0.1",
13
+ "@types/node": "25.3.3",
14
+ "effect": "3.21.2",
15
+ "eslint": "10.0.2",
16
+ "prettier": "3.8.1",
17
+ "tsdown": "0.20.3",
18
+ "tsx": "4.21.0",
19
+ "typescript": "5.9.3",
20
+ "typescript-eslint": "8.56.1",
21
+ "vitest": "3.2.4"
22
+ },
23
+ "engines": {
24
+ "node": ">=22"
25
+ },
23
26
  "exports": {
24
27
  ".": {
25
28
  "types": "./dist/index.d.ts",
@@ -32,46 +35,42 @@
32
35
  "./package.json": "./package.json",
33
36
  "./internal/*": null
34
37
  },
38
+ "files": [
39
+ "CHANGELOG.md",
40
+ "LICENSE",
41
+ "README.md",
42
+ "dist",
43
+ "package.json",
44
+ "src"
45
+ ],
46
+ "homepage": "https://confect.dev",
35
47
  "keywords": [
36
- "effect",
37
- "convex"
48
+ "convex",
49
+ "effect"
38
50
  ],
39
- "author": "RJ Dellecese",
40
51
  "license": "ISC",
41
- "devDependencies": {
42
- "@ark/attest": "0.56.0",
43
- "@effect/vitest": "0.29.0",
44
- "@eslint/js": "10.0.1",
45
- "@types/node": "25.3.3",
46
- "effect": "3.21.2",
47
- "eslint": "10.0.2",
48
- "prettier": "3.8.1",
49
- "tsdown": "0.20.3",
50
- "tsx": "4.21.0",
51
- "typescript": "5.9.3",
52
- "typescript-eslint": "8.56.1",
53
- "vitest": "3.2.4"
54
- },
52
+ "main": "./dist/index.js",
53
+ "module": "./dist/index.js",
55
54
  "peerDependencies": {
56
55
  "convex": "1.39.1",
57
56
  "effect": "^3.21.2"
58
57
  },
59
- "engines": {
60
- "node": ">=22",
61
- "pnpm": ">=10"
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "https://github.com/rjdellecese/confect.git"
62
61
  },
63
- "main": "./dist/index.js",
64
- "module": "./dist/index.js",
62
+ "sideEffects": false,
63
+ "type": "module",
65
64
  "types": "./dist/index.d.ts",
66
65
  "scripts": {
66
+ "bench": "tsx test/Refs.bench.ts",
67
67
  "build": "tsdown --config-loader unrun",
68
+ "clean": "rm -rf dist coverage node_modules",
68
69
  "dev": "tsdown --watch",
69
- "test": "vitest run",
70
- "typecheck": "tsc --noEmit --project tsconfig.json",
71
70
  "fix": "prettier --write . && eslint --fix . --max-warnings=0",
72
71
  "format": "prettier --check .",
73
72
  "lint": "eslint . --max-warnings=0",
74
- "bench": "tsx test/Refs.bench.ts",
75
- "clean": "rm -rf dist coverage node_modules"
73
+ "test": "vitest run",
74
+ "typecheck": "tsc --noEmit --project tsconfig.json"
76
75
  }
77
76
  }
@@ -1,6 +1,7 @@
1
1
  import type { DefaultFunctionArgs } from "convex/server";
2
2
  import type { Schema } from "effect";
3
3
  import { Data } from "effect";
4
+ import * as Lazy from "./Lazy";
4
5
 
5
6
  export type FunctionProvenance = Data.TaggedEnum<{
6
7
  Confect: {
@@ -43,20 +44,41 @@ export interface AnyConvex extends Convex<DefaultFunctionArgs, any> {}
43
44
 
44
45
  export const FunctionProvenance = Data.taggedEnum<FunctionProvenance>();
45
46
 
47
+ /**
48
+ * Build a `Confect` provenance from lazy schema thunks. `args`, `returns`,
49
+ * and `error` are exposed as sync lazy memoised getters (via {@link Lazy.defineProperty})
50
+ * that only evaluate their thunk on first access, mirroring how `Table`
51
+ * defers `Fields`/`Doc`/`tableDefinition`. This keeps importing the assembled
52
+ * `_generated/spec.ts` cheap — no `Schema.Struct(...)` / `Schema.Array(...)`
53
+ * work runs at module load; it is deferred to the first invocation that
54
+ * actually compiles validators or runs a codec.
55
+ *
56
+ * The object is built by hand rather than through `FunctionProvenance.Confect`
57
+ * because the `Data` constructor copies its input with `Object.assign`, which
58
+ * would force the getters at construction time and defeat the laziness.
59
+ * `error` is only installed when an `errorThunk` is provided, so its absence
60
+ * is observable via `"error" in provenance` without forcing anything; nothing
61
+ * relies on `Data`'s structural `Equal`/`Hash` for provenance values.
62
+ */
46
63
  export const Confect = <
47
64
  Args extends Schema.Schema.AnyNoContext,
48
65
  Returns extends Schema.Schema.AnyNoContext,
49
66
  Error extends Schema.Schema.AnyNoContext = never,
50
67
  >(
51
- args: Args,
52
- returns: Returns,
53
- error?: Error,
54
- ) =>
55
- FunctionProvenance.Confect({
56
- args,
57
- returns,
58
- ...(error !== undefined ? { error } : {}),
59
- });
68
+ args: () => Args,
69
+ returns: () => Returns,
70
+ error?: () => Error,
71
+ ): Confect<Args, Returns, Error> => {
72
+ const provenance = { _tag: "Confect" as const };
73
+
74
+ Lazy.defineProperty(provenance, "args", args);
75
+ Lazy.defineProperty(provenance, "returns", returns);
76
+ if (error !== undefined) {
77
+ Lazy.defineProperty(provenance, "error", error);
78
+ }
79
+
80
+ return provenance as Confect<Args, Returns, Error>;
81
+ };
60
82
 
61
83
  export const Convex = <_Args extends DefaultFunctionArgs, _Returns>() =>
62
84
  FunctionProvenance.Convex(
@@ -8,7 +8,7 @@ import type {
8
8
  import type { Schema } from "effect";
9
9
  import { Predicate } from "effect";
10
10
  import * as FunctionProvenance from "./FunctionProvenance";
11
- import { validateConfectFunctionIdentifier } from "./internal/utils";
11
+ import { validateConfectFunctionIdentifier } from "./Identifier";
12
12
  import * as RuntimeAndFunctionType from "./RuntimeAndFunctionType";
13
13
 
14
14
  export const TypeId = "@confect/core/FunctionSpec";
@@ -225,9 +225,9 @@ const make =
225
225
  error,
226
226
  }: {
227
227
  name: Name_;
228
- args: Args_;
229
- returns: Returns_;
230
- error?: Error_;
228
+ args: () => Args_;
229
+ returns: () => Returns_;
230
+ error?: () => Error_;
231
231
  }): FunctionSpec<
232
232
  RuntimeAndFunctionType_,
233
233
  FunctionVisibility_,
package/src/GroupSpec.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Predicate, Record } from "effect";
2
2
  import type * as FunctionSpec from "./FunctionSpec";
3
3
  import type * as RuntimeAndFunctionType from "./RuntimeAndFunctionType";
4
- import { validateConfectFunctionIdentifier } from "./internal/utils";
4
+ import { validateConfectFunctionIdentifier } from "./Identifier";
5
5
 
6
6
  export const TypeId = "@confect/core/GroupSpec";
7
7
  export type TypeId = typeof TypeId;
@@ -56,6 +56,12 @@ const RESERVED_CONVEX_FILE_NAMES = new Set(["schema", "http", "crons"]);
56
56
 
57
57
  const jsIdentifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
58
58
 
59
+ // Stricter than `jsIdentifierRegex`: tables cannot start with `_` (Convex
60
+ // reserves leading underscores for system tables) or `$` (Convex's table
61
+ // naming grammar does not accept it). Letters/digits/underscore only,
62
+ // letter-leading.
63
+ const tableNameRegex = /^[a-zA-Z][a-zA-Z0-9_]*$/;
64
+
59
65
  const isReservedJsIdentifier = (identifier: string) =>
60
66
  RESERVED_JS_IDENTIFIERS.has(identifier);
61
67
 
@@ -65,6 +71,9 @@ const isReservedConvexFileName = (fileName: string) =>
65
71
  const matchesJsIdentifierPattern = (identifier: string) =>
66
72
  jsIdentifierRegex.test(identifier);
67
73
 
74
+ const matchesTableNamePattern = (identifier: string) =>
75
+ tableNameRegex.test(identifier);
76
+
68
77
  export const validateConfectFunctionIdentifier = (identifier: string) => {
69
78
  if (!matchesJsIdentifierPattern(identifier)) {
70
79
  throw new Error(
@@ -84,3 +93,28 @@ export const validateConfectFunctionIdentifier = (identifier: string) => {
84
93
  );
85
94
  }
86
95
  };
96
+
97
+ /**
98
+ * Validate that `identifier` is suitable as a Convex table name (and, equivalently,
99
+ * as a `confect/tables/<identifier>.ts` filename).
100
+ *
101
+ * Rules:
102
+ * - Must match `/^[A-Za-z][A-Za-z0-9_]*$/` — letter-leading, alphanumeric plus
103
+ * underscore. No `$` (not a valid Convex table name character); no leading
104
+ * `_` (Convex reserves `_<name>` for its system tables).
105
+ * - Must not be a reserved JavaScript identifier, so the name can also be used
106
+ * as a binding name in generated code without escaping.
107
+ */
108
+ export const validateConfectTableIdentifier = (identifier: string) => {
109
+ if (!matchesTableNamePattern(identifier)) {
110
+ throw new Error(
111
+ `Expected a valid Confect table identifier, but received: "${identifier}". Valid table identifiers must start with a letter and can only contain letters, numbers, and underscores. Leading underscores are reserved for Convex system tables.`,
112
+ );
113
+ }
114
+
115
+ if (isReservedJsIdentifier(identifier)) {
116
+ throw new Error(
117
+ `Expected a valid Confect table identifier, but received: "${identifier}". "${identifier}" is a reserved JavaScript identifier.`,
118
+ );
119
+ }
120
+ };
package/src/Lazy.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Install a lazy memoised property on `target`. The first access runs
3
+ * `compute()` and replaces the getter with a plain, non-writable data
4
+ * property whose value is the computed result. Subsequent accesses hit
5
+ * the V8 fast path for own data properties — no function call, identical
6
+ * returned reference — so first and second-and-subsequent accesses are
7
+ * observably indistinguishable.
8
+ *
9
+ * The replacement property is `enumerable: true` so the lazy property
10
+ * still participates in `Object.keys` / `JSON.stringify` after it
11
+ * materialises, matching the shape of a plain data property. The property
12
+ * is also `enumerable` before materialising, so presence checks
13
+ * (`"key" in target`, `Object.hasOwn(target, key)`) observe it without
14
+ * forcing the computation.
15
+ *
16
+ * This is the single shared implementation consumed across packages (e.g.
17
+ * `@confect/core`'s lazy `FunctionSpec` schemas and `@confect/server`'s lazy
18
+ * `Table` `Fields` / `Doc` / `tableDefinition`), so there is no chance of the
19
+ * two drifting apart.
20
+ */
21
+ export const defineProperty = <T extends object, K extends PropertyKey>(
22
+ target: T,
23
+ key: K,
24
+ compute: () => unknown,
25
+ ): void => {
26
+ Object.defineProperty(target, key, {
27
+ configurable: true,
28
+ enumerable: true,
29
+ get(this: T) {
30
+ const value = compute();
31
+ Object.defineProperty(this, key, {
32
+ value,
33
+ writable: false,
34
+ enumerable: true,
35
+ configurable: false,
36
+ });
37
+ return value;
38
+ },
39
+ });
40
+ };
package/src/Ref.ts CHANGED
@@ -190,17 +190,29 @@ export const make = <FunctionSpec_ extends FunctionSpec.AnyWithProps>(
190
190
  export const getConvexFunctionName = (ref: Any): string =>
191
191
  `${ref.functionNamespace}:${ref.functionSpec.name}`;
192
192
 
193
+ const functionReferenceCache = new Map<string, FunctionReference<Any>>();
194
+
193
195
  export const getFunctionReference = <Ref_ extends Any>(
194
196
  ref: Ref_,
195
- ): FunctionReference<Ref_> =>
196
- makeFunctionReference(getConvexFunctionName(ref)) as FunctionReference<Ref_>;
197
+ ): FunctionReference<Ref_> => {
198
+ const functionName = getConvexFunctionName(ref);
199
+
200
+ const cached = functionReferenceCache.get(functionName);
201
+ if (cached !== undefined) {
202
+ return cached as FunctionReference<Ref_>;
203
+ }
204
+
205
+ const functionReference = makeFunctionReference(functionName);
206
+ functionReferenceCache.set(functionName, functionReference);
207
+
208
+ return functionReference as FunctionReference<Ref_>;
209
+ };
197
210
 
198
211
  export const hasErrorSchema = (ref: Any): boolean =>
199
212
  Match.value(ref.functionSpec.functionProvenance).pipe(
200
213
  Match.tag(
201
214
  "Confect",
202
- (confectFunctionProvenance) =>
203
- confectFunctionProvenance.error !== undefined,
215
+ (confectFunctionProvenance) => "error" in confectFunctionProvenance,
204
216
  ),
205
217
  Match.tag("Convex", () => false),
206
218
  Match.exhaustive,
@@ -296,7 +308,7 @@ export const decodeError = <Ref_ extends Any>(
296
308
  ): Effect.Effect<Option.Option<Error<Ref_>>, ParseResult.ParseError> =>
297
309
  Match.value(ref.functionSpec.functionProvenance).pipe(
298
310
  Match.tag("Confect", (confectFunctionProvenance) =>
299
- confectFunctionProvenance.error !== undefined
311
+ "error" in confectFunctionProvenance
300
312
  ? Effect.map(
301
313
  Schema.decode(confectFunctionProvenance.error)(encodedError),
302
314
  Option.some,
@@ -317,7 +329,7 @@ export const decodeErrorSync = <Ref_ extends Any>(
317
329
  ): Option.Option<Error<Ref_>> =>
318
330
  Match.value(ref.functionSpec.functionProvenance).pipe(
319
331
  Match.tag("Confect", (confectFunctionProvenance) =>
320
- confectFunctionProvenance.error !== undefined
332
+ "error" in confectFunctionProvenance
321
333
  ? Option.some(
322
334
  Schema.decodeSync(confectFunctionProvenance.error)(
323
335
  encodedError,
@@ -336,7 +348,7 @@ export const maybeDecodeErrorSync = <Ref_ extends Any>(
336
348
  isConvexError(error)
337
349
  ? Match.value(ref.functionSpec.functionProvenance).pipe(
338
350
  Match.tag("Confect", (confectFunctionProvenance) =>
339
- confectFunctionProvenance.error !== undefined
351
+ "error" in confectFunctionProvenance
340
352
  ? Schema.decodeSync(confectFunctionProvenance.error)(error.data)
341
353
  : error,
342
354
  ),
package/src/Spec.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { Array, Option, Predicate, Record, String } from "effect";
1
+ import { Array, Option, Predicate, Record } from "effect";
2
2
  import * as GroupSpec from "./GroupSpec";
3
3
  import type * as RuntimeAndFunctionType from "./RuntimeAndFunctionType";
4
- import { validateConfectFunctionIdentifier } from "./internal/utils";
5
4
 
6
5
  export const TypeId = "@confect/core/Spec";
7
6
  export type TypeId = typeof TypeId;
@@ -33,13 +32,6 @@ export interface Spec<
33
32
  GroupName
34
33
  >;
35
34
  };
36
- /**
37
- * Mapping from an imported leaf `GroupSpec` reference to its full dot-path
38
- * within this spec tree. Populated by codegen-emitted `_generated/spec.ts`
39
- * via {@link Spec#addPath}; consumed by `FunctionImpl.make` /
40
- * `GroupImpl.make` to resolve a spec's location without walking the tree.
41
- */
42
- readonly paths: ReadonlyMap<GroupSpec.AnyWithProps, string>;
43
35
 
44
36
  add<Group extends GroupSpec.AnyWithPropsWithRuntime<Runtime>>(
45
37
  group: Group,
@@ -52,18 +44,6 @@ export interface Spec<
52
44
  name: Name,
53
45
  group: Group,
54
46
  ): Spec<Runtime, Groups_ | GroupSpec.NamedAt<Group, Name>>;
55
-
56
- /**
57
- * Register the imported leaf `group` at `path` within this spec's path
58
- * mapping. Returns a new `Spec` with one additional entry. The tree shape
59
- * (`groups`) is unaffected — registration and tree assembly are
60
- * independent steps, both performed by codegen in `_generated/spec.ts`.
61
- *
62
- * Re-registering the same group with the same path is a no-op (cheap
63
- * defense for codegen watch-mode re-imports). Re-registering with a
64
- * different path throws.
65
- */
66
- addPath(group: GroupSpec.AnyWithProps, path: string): Spec<Runtime, Groups_>;
67
47
  }
68
48
 
69
49
  export interface Any {
@@ -82,25 +62,6 @@ export interface AnyWithPropsWithRuntime<
82
62
  export type Groups<Spec_ extends AnyWithProps> =
83
63
  Spec_["groups"][keyof Spec_["groups"]];
84
64
 
85
- const validatePath = (path: string): void => {
86
- if (path.length === 0) {
87
- throw new Error(
88
- "Expected a non-empty Confect group path, but received an empty string.",
89
- );
90
- }
91
-
92
- const segments = String.split(path, ".");
93
-
94
- for (const segment of segments) {
95
- if (segment.length === 0) {
96
- throw new Error(
97
- `Expected a Confect group path made of dot-separated identifier segments, but received: "${path}".`,
98
- );
99
- }
100
- validateConfectFunctionIdentifier(segment);
101
- }
102
- };
103
-
104
65
  const Proto = {
105
66
  [TypeId]: TypeId,
106
67
 
@@ -108,7 +69,6 @@ const Proto = {
108
69
  return makeProto({
109
70
  runtime: this.runtime,
110
71
  groups: Record.set(this.groups, group.name, group),
111
- paths: this.paths,
112
72
  });
113
73
  },
114
74
 
@@ -120,30 +80,6 @@ const Proto = {
120
80
  return makeProto({
121
81
  runtime: this.runtime,
122
82
  groups: Record.set(this.groups, name, GroupSpec.withName(name, group)),
123
- paths: this.paths,
124
- });
125
- },
126
-
127
- addPath(this: AnyWithProps, group: GroupSpec.AnyWithProps, path: string) {
128
- validatePath(path);
129
-
130
- const existing = this.paths.get(group);
131
- if (existing !== undefined) {
132
- if (existing === path) {
133
- return this;
134
- }
135
- throw new Error(
136
- `Spec.addPath: the provided GroupSpec is already registered at "${existing}", but was re-registered at "${path}". Each GroupSpec must have at most one path.`,
137
- );
138
- }
139
-
140
- const nextPaths = new Map(this.paths);
141
- nextPaths.set(group, path);
142
-
143
- return makeProto({
144
- runtime: this.runtime,
145
- groups: this.groups,
146
- paths: nextPaths,
147
83
  });
148
84
  },
149
85
  };
@@ -154,32 +90,26 @@ const makeProto = <
154
90
  >({
155
91
  runtime,
156
92
  groups,
157
- paths,
158
93
  }: {
159
94
  runtime: Runtime;
160
95
  groups: Record.ReadonlyRecord<string, Groups_>;
161
- paths: ReadonlyMap<GroupSpec.AnyWithProps, string>;
162
96
  }): Spec<Runtime, Groups_> =>
163
97
  Object.assign(Object.create(Proto), {
164
98
  runtime,
165
99
  groups,
166
- paths,
167
100
  });
168
101
 
169
- const emptyPaths = (): ReadonlyMap<GroupSpec.AnyWithProps, string> => new Map();
170
-
171
102
  export const make = (): Spec<"Convex"> =>
172
- makeProto({ runtime: "Convex", groups: {}, paths: emptyPaths() });
103
+ makeProto({ runtime: "Convex", groups: {} });
173
104
 
174
105
  export const makeNode = (): Spec<"Node"> =>
175
- makeProto({ runtime: "Node", groups: {}, paths: emptyPaths() });
106
+ makeProto({ runtime: "Node", groups: {} });
176
107
 
177
108
  /**
178
- * Merges a Convex spec with an optional Node spec for use with `Api.make`.
109
+ * Merges a Convex spec with an optional Node spec into a single assembled
110
+ * spec (used by codegen to build `Refs.make` and to enumerate function paths).
179
111
  * When `nodeSpec` is provided, its groups are merged under a "node" namespace,
180
- * mirroring the structure used by `Refs.make`. The node spec's `paths`
181
- * entries are re-prefixed with `"node."` so they continue to identify the
182
- * same leaves at their new positions in the merged tree.
112
+ * mirroring the structure used by `Refs.make`.
183
113
  */
184
114
  export const merge = <
185
115
  ConvexSpec extends AnyWithPropsWithRuntime<"Convex">,
@@ -202,16 +132,8 @@ export const merge = <
202
132
  }),
203
133
  );
204
134
 
205
- const paths = new Map(convexSpec.paths);
206
- if (nodeSpec !== undefined) {
207
- for (const [group, path] of nodeSpec.paths) {
208
- paths.set(group, `node.${path}`);
209
- }
210
- }
211
-
212
135
  return Object.assign(Object.create(Proto), {
213
136
  runtime: "Convex" as const,
214
137
  groups,
215
- paths,
216
138
  }) as AnyWithProps;
217
139
  };
package/src/index.ts CHANGED
@@ -3,6 +3,8 @@ export * as FunctionSpec from "./FunctionSpec";
3
3
  export * as GenericId from "./GenericId";
4
4
  export * as GroupPath from "./GroupPath";
5
5
  export * as GroupSpec from "./GroupSpec";
6
+ export * as Identifier from "./Identifier";
7
+ export * as Lazy from "./Lazy";
6
8
  export * as PaginationResult from "./PaginationResult";
7
9
  export * as Ref from "./Ref";
8
10
  export * as Refs from "./Refs";
@@ -1,5 +0,0 @@
1
- //#region src/internal/utils.d.ts
2
- declare const validateConfectFunctionIdentifier: (identifier: string) => void;
3
- //#endregion
4
- export { validateConfectFunctionIdentifier };
5
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","names":[],"sources":["../../src/internal/utils.ts"],"mappings":";cAmEa,iCAAA,GAAqC,UAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../src/internal/utils.ts"],"sourcesContent":["const RESERVED_JS_IDENTIFIERS = new Set([\n // Reserved keywords\n \"break\",\n \"case\",\n \"catch\",\n \"class\",\n \"const\",\n \"continue\",\n \"debugger\",\n \"default\",\n \"delete\",\n \"do\",\n \"else\",\n \"export\",\n \"extends\",\n \"finally\",\n \"for\",\n \"function\",\n \"if\",\n \"import\",\n \"in\",\n \"instanceof\",\n \"new\",\n \"return\",\n \"super\",\n \"switch\",\n \"this\",\n \"throw\",\n \"try\",\n \"typeof\",\n \"var\",\n \"void\",\n \"while\",\n \"with\",\n \"yield\",\n // Future reserved keywords\n \"await\",\n \"enum\",\n \"implements\",\n \"interface\",\n \"let\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n \"static\",\n // Literal values that cannot be reassigned\n \"null\",\n \"true\",\n \"false\",\n // Global objects that shouldn't be shadowed\n \"undefined\",\n]);\n\nconst RESERVED_CONVEX_FILE_NAMES = new Set([\"schema\", \"http\", \"crons\"]);\n\nconst jsIdentifierRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;\n\nconst isReservedJsIdentifier = (identifier: string) =>\n RESERVED_JS_IDENTIFIERS.has(identifier);\n\nconst isReservedConvexFileName = (fileName: string) =>\n RESERVED_CONVEX_FILE_NAMES.has(fileName);\n\nconst matchesJsIdentifierPattern = (identifier: string) =>\n jsIdentifierRegex.test(identifier);\n\nexport const validateConfectFunctionIdentifier = (identifier: string) => {\n if (!matchesJsIdentifierPattern(identifier)) {\n throw new Error(\n `Expected a valid Confect function identifier, but received: \"${identifier}\". Valid identifiers must start with a letter, underscore, or dollar sign, and can only contain letters, numbers, underscores, or dollar signs.`,\n );\n }\n\n if (isReservedJsIdentifier(identifier)) {\n throw new Error(\n `Expected a valid Confect function identifier, but received: \"${identifier}\". \"${identifier}\" is a reserved JavaScript identifier.`,\n );\n }\n\n if (isReservedConvexFileName(identifier)) {\n throw new Error(\n `Expected a valid Confect function identifier, but received: \"${identifier}\". \"${identifier}\" is a reserved Convex file name.`,\n );\n }\n};\n"],"mappings":";AAAA,MAAM,0BAA0B,IAAI,IAAI;CAEtC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA;CACD,CAAC;AAEF,MAAM,6BAA6B,IAAI,IAAI;CAAC;CAAU;CAAQ;CAAQ,CAAC;AAEvE,MAAM,oBAAoB;AAE1B,MAAM,0BAA0B,eAC9B,wBAAwB,IAAI,WAAW;AAEzC,MAAM,4BAA4B,aAChC,2BAA2B,IAAI,SAAS;AAE1C,MAAM,8BAA8B,eAClC,kBAAkB,KAAK,WAAW;AAEpC,MAAa,qCAAqC,eAAuB;AACvE,KAAI,CAAC,2BAA2B,WAAW,CACzC,OAAM,IAAI,MACR,gEAAgE,WAAW,iJAC5E;AAGH,KAAI,uBAAuB,WAAW,CACpC,OAAM,IAAI,MACR,gEAAgE,WAAW,MAAM,WAAW,wCAC7F;AAGH,KAAI,yBAAyB,WAAW,CACtC,OAAM,IAAI,MACR,gEAAgE,WAAW,MAAM,WAAW,mCAC7F"}