@confect/core 9.0.0-next.2 → 9.0.0-next.3
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/CHANGELOG.md +26 -0
- package/dist/FunctionProvenance.d.ts +30 -30
- package/dist/FunctionProvenance.d.ts.map +1 -1
- package/dist/GroupSpec.js +6 -1
- package/dist/GroupSpec.js.map +1 -1
- package/dist/Spec.d.ts +26 -6
- package/dist/Spec.d.ts.map +1 -1
- package/dist/Spec.js +44 -9
- package/dist/Spec.js.map +1 -1
- package/package.json +1 -1
- package/src/GroupSpec.ts +6 -3
- package/src/Spec.ts +83 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @confect/core
|
|
2
2
|
|
|
3
|
+
## 9.0.0-next.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 6d85210: Resolve `FunctionImpl` / `GroupImpl` group paths via an immutable `paths` map on `Spec` instead of identity-walking the assembled tree.
|
|
8
|
+
|
|
9
|
+
### Why
|
|
10
|
+
|
|
11
|
+
Since `9.0.0-next.1`, codegen has wrapped every parent leaf that has sibling subdirectory specs in `<parent>.addGroupAt("child", <child>)`. Because `GroupSpec.addGroupAt` is immutable, that produced a fresh object in the assembled tree, while the parent's `*.impl.ts` continued to hold a reference to the original imported leaf. The runtime resolver compared by `===`, so every such impl failed `validateImpl` with "Could not resolve group path for the provided GroupSpec." Child impls happened to work only because `GroupSpec.withName` was secretly mutating its argument in place to keep the child's identity stable — an asymmetry that was load-bearing for one half of the API and broken for the other.
|
|
12
|
+
|
|
13
|
+
### What changed
|
|
14
|
+
- `@confect/core/Spec` carries a new `readonly paths: ReadonlyMap<GroupSpec.AnyWithProps, string>` field and exposes a chainable `Spec#addPath(group, path)` builder. `add` / `addAt` / `merge` propagate `paths` unchanged; `merge` re-prefixes a node spec's entries with `"node."` to match the merged tree.
|
|
15
|
+
- `@confect/core/GroupSpec.withName` is now pure: it returns a fresh copy when the name differs and no longer rewrites the input in place. No new identity-tracking machinery is introduced.
|
|
16
|
+
- `@confect/server/FunctionImpl.make` and `GroupImpl.make` resolve their group path via `api.spec.paths.get(group)` — an O(1) map lookup instead of a tree walk — and throw a clearer error pointing at `Spec.addPath` when the spec hasn't been registered.
|
|
17
|
+
- `@confect/server/GroupPath` (the old identity-based resolver) is deleted.
|
|
18
|
+
- `@confect/cli` codegen emits one `.addPath(<binding>, "<dot.path>")` call per leaf in `_generated/spec.ts` (and `_generated/nodeSpec.ts`) so the imported leaves carry their full paths into the assembled spec value.
|
|
19
|
+
|
|
20
|
+
### User-facing impact
|
|
21
|
+
- Spec authoring (`*.spec.ts`) and impl authoring (`*.impl.ts`) APIs are unchanged. `FunctionImpl.make(api, spec, name, handler)` and `GroupImpl.make(api, spec)` keep their exact signatures.
|
|
22
|
+
- Generated `_generated/spec.ts` (and `_generated/nodeSpec.ts`) pick up one `.addPath(...)` chain entry per leaf on the next `confect codegen` run. The shape is fully immutable — no module-load mutation, no hidden side effects.
|
|
23
|
+
- Hand-rolled tests that construct a `Spec` and pass it to `Api.make` must now also call `.addPath(spec, "dot.path")` for any group they intend to look up.
|
|
24
|
+
|
|
25
|
+
### Fixes
|
|
26
|
+
|
|
27
|
+
This eliminates the runtime regression introduced in `9.0.0-next.1` for any project layout where a `confect/{path}.spec.ts` declares functions alongside a sibling `confect/{path}/` subdirectory of further specs.
|
|
28
|
+
|
|
3
29
|
## 9.0.0-next.2
|
|
4
30
|
|
|
5
31
|
## 9.0.0-next.1
|
|
@@ -28,76 +28,76 @@ interface Convex<Args extends DefaultFunctionArgs, Returns> {
|
|
|
28
28
|
}
|
|
29
29
|
interface AnyConvex extends Convex<DefaultFunctionArgs, any> {}
|
|
30
30
|
declare const FunctionProvenance: {
|
|
31
|
-
readonly Convex: Data.Case.Constructor<{
|
|
32
|
-
readonly _tag: "Convex";
|
|
33
|
-
readonly _args: DefaultFunctionArgs;
|
|
34
|
-
readonly _returns: any;
|
|
35
|
-
}, "_tag">;
|
|
36
31
|
readonly Confect: Data.Case.Constructor<{
|
|
37
32
|
readonly _tag: "Confect";
|
|
38
33
|
readonly args: Schema.Schema.AnyNoContext;
|
|
39
34
|
readonly returns: Schema.Schema.AnyNoContext;
|
|
40
35
|
readonly error?: Schema.Schema.AnyNoContext;
|
|
41
36
|
}, "_tag">;
|
|
42
|
-
readonly
|
|
37
|
+
readonly Convex: Data.Case.Constructor<{
|
|
43
38
|
readonly _tag: "Convex";
|
|
44
39
|
readonly _args: DefaultFunctionArgs;
|
|
45
40
|
readonly _returns: any;
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
}> | Extract<{
|
|
41
|
+
}, "_tag">;
|
|
42
|
+
readonly $is: <Tag extends "Confect" | "Convex">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
49
43
|
readonly _tag: "Confect";
|
|
50
44
|
readonly args: Schema.Schema.AnyNoContext;
|
|
51
45
|
readonly returns: Schema.Schema.AnyNoContext;
|
|
52
46
|
readonly error?: Schema.Schema.AnyNoContext;
|
|
53
47
|
}, {
|
|
54
48
|
readonly _tag: Tag;
|
|
49
|
+
}> | Extract<{
|
|
50
|
+
readonly _tag: "Convex";
|
|
51
|
+
readonly _args: DefaultFunctionArgs;
|
|
52
|
+
readonly _returns: any;
|
|
53
|
+
}, {
|
|
54
|
+
readonly _tag: Tag;
|
|
55
55
|
}>;
|
|
56
56
|
readonly $match: {
|
|
57
57
|
<const Cases extends {
|
|
58
|
-
readonly Convex: (args: {
|
|
59
|
-
readonly _tag: "Convex";
|
|
60
|
-
readonly _args: DefaultFunctionArgs;
|
|
61
|
-
readonly _returns: any;
|
|
62
|
-
}) => any;
|
|
63
58
|
readonly Confect: (args: {
|
|
64
59
|
readonly _tag: "Confect";
|
|
65
60
|
readonly args: Schema.Schema.AnyNoContext;
|
|
66
61
|
readonly returns: Schema.Schema.AnyNoContext;
|
|
67
62
|
readonly error?: Schema.Schema.AnyNoContext;
|
|
68
63
|
}) => any;
|
|
69
|
-
}>(cases: Cases & { [K in Exclude<keyof Cases, "Convex" | "Confect">]: never }): (value: {
|
|
70
|
-
readonly _tag: "Convex";
|
|
71
|
-
readonly _args: DefaultFunctionArgs;
|
|
72
|
-
readonly _returns: any;
|
|
73
|
-
} | {
|
|
74
|
-
readonly _tag: "Confect";
|
|
75
|
-
readonly args: Schema.Schema.AnyNoContext;
|
|
76
|
-
readonly returns: Schema.Schema.AnyNoContext;
|
|
77
|
-
readonly error?: Schema.Schema.AnyNoContext;
|
|
78
|
-
}) => effect_Unify0.Unify<ReturnType<Cases["Convex" | "Confect"]>>;
|
|
79
|
-
<const Cases extends {
|
|
80
64
|
readonly Convex: (args: {
|
|
81
65
|
readonly _tag: "Convex";
|
|
82
66
|
readonly _args: DefaultFunctionArgs;
|
|
83
67
|
readonly _returns: any;
|
|
84
68
|
}) => any;
|
|
69
|
+
}>(cases: Cases & { [K in Exclude<keyof Cases, "Confect" | "Convex">]: never }): (value: {
|
|
70
|
+
readonly _tag: "Confect";
|
|
71
|
+
readonly args: Schema.Schema.AnyNoContext;
|
|
72
|
+
readonly returns: Schema.Schema.AnyNoContext;
|
|
73
|
+
readonly error?: Schema.Schema.AnyNoContext;
|
|
74
|
+
} | {
|
|
75
|
+
readonly _tag: "Convex";
|
|
76
|
+
readonly _args: DefaultFunctionArgs;
|
|
77
|
+
readonly _returns: any;
|
|
78
|
+
}) => effect_Unify0.Unify<ReturnType<Cases["Confect" | "Convex"]>>;
|
|
79
|
+
<const Cases extends {
|
|
85
80
|
readonly Confect: (args: {
|
|
86
81
|
readonly _tag: "Confect";
|
|
87
82
|
readonly args: Schema.Schema.AnyNoContext;
|
|
88
83
|
readonly returns: Schema.Schema.AnyNoContext;
|
|
89
84
|
readonly error?: Schema.Schema.AnyNoContext;
|
|
90
85
|
}) => any;
|
|
86
|
+
readonly Convex: (args: {
|
|
87
|
+
readonly _tag: "Convex";
|
|
88
|
+
readonly _args: DefaultFunctionArgs;
|
|
89
|
+
readonly _returns: any;
|
|
90
|
+
}) => any;
|
|
91
91
|
}>(value: {
|
|
92
|
-
readonly _tag: "Convex";
|
|
93
|
-
readonly _args: DefaultFunctionArgs;
|
|
94
|
-
readonly _returns: any;
|
|
95
|
-
} | {
|
|
96
92
|
readonly _tag: "Confect";
|
|
97
93
|
readonly args: Schema.Schema.AnyNoContext;
|
|
98
94
|
readonly returns: Schema.Schema.AnyNoContext;
|
|
99
95
|
readonly error?: Schema.Schema.AnyNoContext;
|
|
100
|
-
}
|
|
96
|
+
} | {
|
|
97
|
+
readonly _tag: "Convex";
|
|
98
|
+
readonly _args: DefaultFunctionArgs;
|
|
99
|
+
readonly _returns: any;
|
|
100
|
+
}, cases: Cases & { [K in Exclude<keyof Cases, "Confect" | "Convex">]: never }): effect_Unify0.Unify<ReturnType<Cases["Confect" | "Convex"]>>;
|
|
101
101
|
};
|
|
102
102
|
};
|
|
103
103
|
declare const Confect: <Args extends Schema.Schema.AnyNoContext, Returns extends Schema.Schema.AnyNoContext, Error extends Schema.Schema.AnyNoContext = never>(args: Args, returns: Returns, error?: Error) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FunctionProvenance.d.ts","names":[],"sources":["../src/FunctionProvenance.ts"],"mappings":";;;;;;;;KAIY,kBAAA,GAAqB,IAAA,CAAK,UAAA;EACpC,OAAA;IACE,IAAA,EAAM,MAAA,CAAO,MAAA,CAAO,YAAA;IACpB,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,YAAA;IACvB,KAAA,GAAQ,MAAA,CAAO,MAAA,CAAO,YAAA;EAAA;EAExB,MAAA;AAAA;AAAA,UAQe,OAAA,cACF,MAAA,CAAO,MAAA,CAAO,YAAA,kBACX,MAAA,CAAO,MAAA,CAAO,YAAA,gBAChB,MAAA,CAAO,MAAA,CAAO,YAAA;EAAA,SAEnB,IAAA;EAAA,SACA,IAAA,EAAM,IAAA;EAAA,SACN,OAAA,EAAS,OAAA;EAAA,SACT,KAAA,GAAQ,KAAA;AAAA;AAAA,UAGF,UAAA,SAAmB,OAAA,CAClC,MAAA,CAAO,MAAA,CAAO,YAAA,EACd,MAAA,CAAO,MAAA,CAAO,YAAA,EACd,MAAA,CAAO,MAAA,CAAO,YAAA;AAAA,UAGC,MAAA,cAAoB,mBAAA;EAAA,SAC1B,IAAA;EAAA,SACA,KAAA,EAAO,IAAA;EAAA,SACP,QAAA,EAAU,OAAA;AAAA;AAAA,UAGJ,SAAA,SAAkB,MAAA,CAAO,mBAAA;AAAA,cAE7B,kBAAA;EAAA;;
|
|
1
|
+
{"version":3,"file":"FunctionProvenance.d.ts","names":[],"sources":["../src/FunctionProvenance.ts"],"mappings":";;;;;;;;KAIY,kBAAA,GAAqB,IAAA,CAAK,UAAA;EACpC,OAAA;IACE,IAAA,EAAM,MAAA,CAAO,MAAA,CAAO,YAAA;IACpB,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,YAAA;IACvB,KAAA,GAAQ,MAAA,CAAO,MAAA,CAAO,YAAA;EAAA;EAExB,MAAA;AAAA;AAAA,UAQe,OAAA,cACF,MAAA,CAAO,MAAA,CAAO,YAAA,kBACX,MAAA,CAAO,MAAA,CAAO,YAAA,gBAChB,MAAA,CAAO,MAAA,CAAO,YAAA;EAAA,SAEnB,IAAA;EAAA,SACA,IAAA,EAAM,IAAA;EAAA,SACN,OAAA,EAAS,OAAA;EAAA,SACT,KAAA,GAAQ,KAAA;AAAA;AAAA,UAGF,UAAA,SAAmB,OAAA,CAClC,MAAA,CAAO,MAAA,CAAO,YAAA,EACd,MAAA,CAAO,MAAA,CAAO,YAAA,EACd,MAAA,CAAO,MAAA,CAAO,YAAA;AAAA,UAGC,MAAA,cAAoB,mBAAA;EAAA,SAC1B,IAAA;EAAA,SACA,KAAA,EAAO,IAAA;EAAA,SACP,QAAA,EAAU,OAAA;AAAA;AAAA,UAGJ,SAAA,SAAkB,MAAA,CAAO,mBAAA;AAAA,cAE7B,kBAAA;EAAA;;mBArCH,MAAA,CAAO,MAAA,CAAO,YAAA;IAAA,kBACX,MAAA,CAAO,MAAA,CAAO,YAAA;IAAA,iBACf,MAAA,CAAO,MAAA,CAAO,YAAA;EAAA;EAAA;;oBAIf,mBAAA;IAAA;;;;mBAND,MAAA,CAAO,MAAA,CAAO,YAAA;IAAA,kBACX,MAAA,CAAO,MAAA,CAAO,YAAA;IAAA,iBACf,MAAA,CAAO,MAAA,CAAO,YAAA;EAAA;IAAA;;;oBAIf,mBAAA;IAAA;;;;;;;;uBAND,MAAA,CAAO,MAAA,CAAO,YAAA;QAAA,kBACX,MAAA,CAAO,MAAA,CAAO,YAAA;QAAA,iBACf,MAAA,CAAO,MAAA,CAAO,YAAA;MAAA;MAAA;;wBAIf,mBAAA;QAAA;;;;qBAND,MAAA,CAAO,MAAA,CAAO,YAAA;MAAA,kBACX,MAAA,CAAO,MAAA,CAAO,YAAA;MAAA,iBACf,MAAA,CAAO,MAAA,CAAO,YAAA;IAAA;MAAA;sBAIf,mBAAA;MAAA;;;;;uBAND,MAAA,CAAO,MAAA,CAAO,YAAA;QAAA,kBACX,MAAA,CAAO,MAAA,CAAO,YAAA;QAAA,iBACf,MAAA,CAAO,MAAA,CAAO,YAAA;MAAA;MAAA;;wBAIf,mBAAA;QAAA;;;;qBAND,MAAA,CAAO,MAAA,CAAO,YAAA;MAAA,kBACX,MAAA,CAAO,MAAA,CAAO,YAAA;MAAA,iBACf,MAAA,CAAO,MAAA,CAAO,YAAA;IAAA;MAAA;sBAIf,mBAAA;MAAA;;;;cAiCE,OAAA,gBACE,MAAA,CAAO,MAAA,CAAO,YAAA,kBACX,MAAA,CAAO,MAAA,CAAO,YAAA,gBAChB,MAAA,CAAO,MAAA,CAAO,YAAA,UAE5B,IAAA,EAAM,IAAA,EACN,OAAA,EAAS,OAAA,EACT,KAAA,GAAQ,KAAA;EAAA;iBA9CA,MAAA,CAAO,MAAA,CAAO,YAAA;EAAA,kBACX,MAAA,CAAO,MAAA,CAAO,YAAA;EAAA,iBACf,MAAA,CAAO,MAAA,CAAO,YAAA;AAAA;AAAA,cAoDb,MAAA,iBAAwB,mBAAA;EAAA;kBAhD1B,mBAAA;EAAA"}
|
package/dist/GroupSpec.js
CHANGED
|
@@ -86,7 +86,12 @@ const withName = (name, group) => {
|
|
|
86
86
|
validateConfectFunctionIdentifier(name);
|
|
87
87
|
const group_ = group;
|
|
88
88
|
if (group_.name === name) return group_;
|
|
89
|
-
return
|
|
89
|
+
return makeProto({
|
|
90
|
+
runtime: group_.runtime,
|
|
91
|
+
name,
|
|
92
|
+
functions: group_.functions,
|
|
93
|
+
groups: group_.groups
|
|
94
|
+
});
|
|
90
95
|
};
|
|
91
96
|
|
|
92
97
|
//#endregion
|
package/dist/GroupSpec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupSpec.js","names":[],"sources":["../src/GroupSpec.ts"],"sourcesContent":["import { Predicate, Record } from \"effect\";\nimport type * as FunctionSpec from \"./FunctionSpec\";\nimport type * as RuntimeAndFunctionType from \"./RuntimeAndFunctionType\";\nimport { validateConfectFunctionIdentifier } from \"./internal/utils\";\n\nexport const TypeId = \"@confect/core/GroupSpec\";\nexport type TypeId = typeof TypeId;\n\nexport const isGroupSpec = (u: unknown): u is AnyWithProps =>\n Predicate.hasProperty(u, TypeId);\n\nexport interface GroupSpec<\n Runtime extends RuntimeAndFunctionType.Runtime,\n Name_ extends string,\n Functions_ extends FunctionSpec.AnyWithPropsWithRuntime<Runtime> = never,\n Groups_ extends AnyWithPropsWithRuntime<Runtime> = never,\n> {\n readonly [TypeId]: TypeId;\n readonly runtime: Runtime;\n readonly name: Name_;\n readonly functions: {\n [FunctionName in FunctionSpec.Name<\n FunctionSpec.AnyWithPropsWithRuntime<Runtime>\n >]: FunctionSpec.WithName<Functions_, FunctionName>;\n };\n readonly groups: {\n [GroupName in Name<Groups_>]: WithName<Groups_, GroupName>;\n };\n\n addFunction<Function extends FunctionSpec.AnyWithPropsWithRuntime<Runtime>>(\n function_: Function,\n ): GroupSpec<Runtime, Name_, Functions_ | Function, Groups_>;\n\n addGroup<Group extends AnyWithPropsWithRuntime<Runtime>>(\n group: Group,\n ): GroupSpec<Runtime, Name_, Functions_, Groups_ | Group>;\n\n addGroupAt<\n const AtName extends string,\n Group extends AnyWithPropsWithRuntime<Runtime>,\n >(\n name: AtName,\n group: Group,\n ): GroupSpec<Runtime, Name_, Functions_, Groups_ | NamedAt<Group, AtName>>;\n}\n\nexport interface Any {\n readonly [TypeId]: TypeId;\n}\n\nexport interface AnyWithProps extends GroupSpec<\n RuntimeAndFunctionType.Runtime,\n string,\n FunctionSpec.AnyWithProps,\n AnyWithProps\n> {}\n\nexport interface AnyWithPropsWithRuntime<\n Runtime extends RuntimeAndFunctionType.Runtime,\n> extends GroupSpec<\n Runtime,\n string,\n FunctionSpec.AnyWithPropsWithRuntime<Runtime>,\n AnyWithPropsWithRuntime<Runtime>\n> {}\n\nexport type Name<Group extends AnyWithProps> = Group[\"name\"];\n\nexport type Functions<Group extends AnyWithProps> =\n Group[\"functions\"][keyof Group[\"functions\"]];\n\nexport type Groups<Group extends AnyWithProps> =\n Group[\"groups\"][keyof Group[\"groups\"]];\n\nexport type GroupNames<Group extends AnyWithProps> = [Groups<Group>] extends [\n never,\n]\n ? never\n : Name<Groups<Group>>;\n\nexport type WithName<\n Group extends AnyWithProps,\n Name_ extends Name<Group>,\n> = Extract<Group, { readonly name: Name_ }>;\n\n/** Assigns a segment name to a leaf group created with {@link make} for typing and refs. */\nexport type NamedAt<Group extends Any, Name_ extends string> = Omit<\n Group,\n \"name\"\n> & {\n readonly name: Name_;\n};\n\nconst Proto = {\n [TypeId]: TypeId,\n\n addFunction<Function extends FunctionSpec.AnyWithProps>(\n this: Any,\n function_: Function,\n ) {\n const this_ = this as AnyWithProps;\n\n return makeProto({\n runtime: this_.runtime,\n name: this_.name,\n functions: Record.set(this_.functions, function_.name, function_),\n groups: this_.groups,\n });\n },\n\n addGroup<Group extends Any>(this: Any, group: Group) {\n const this_ = this as AnyWithProps;\n const group_ = group as unknown as AnyWithProps;\n\n return makeProto({\n runtime: this_.runtime,\n name: this_.name,\n functions: this_.functions,\n groups: Record.set(this_.groups, group_.name, group_),\n });\n },\n\n addGroupAt<Group extends Any>(this: Any, name: string, group: Group) {\n const this_ = this as AnyWithProps;\n const group_ = group as unknown as AnyWithProps;\n\n return makeProto({\n runtime: this_.runtime,\n name: this_.name,\n functions: this_.functions,\n groups: Record.set(this_.groups, name, withName(name, group_)),\n });\n },\n};\n\nconst makeProto = <\n Runtime extends RuntimeAndFunctionType.Runtime,\n Name_ extends string,\n Functions_ extends FunctionSpec.AnyWithPropsWithRuntime<Runtime>,\n Groups_ extends AnyWithPropsWithRuntime<Runtime>,\n>({\n runtime,\n name,\n functions,\n groups,\n}: {\n runtime: Runtime;\n name: Name_;\n functions: Record.ReadonlyRecord<string, Functions_>;\n groups: Record.ReadonlyRecord<string, Groups_>;\n}): GroupSpec<Runtime, Name_, Functions_, Groups_> =>\n Object.assign(Object.create(Proto), {\n runtime,\n name,\n functions,\n groups,\n });\n\nexport const make = (): GroupSpec<\"Convex\", \"\"> =>\n makeProto({\n runtime: \"Convex\",\n name: \"\",\n functions: Record.empty(),\n groups: Record.empty(),\n });\n\nexport const makeAt = <const Name_ extends string>(\n name: Name_,\n): GroupSpec<\"Convex\", Name_> => {\n validateConfectFunctionIdentifier(name);\n\n return makeProto({\n runtime: \"Convex\",\n name,\n functions: Record.empty(),\n groups: Record.empty(),\n });\n};\n\nexport const makeNode = (): GroupSpec<\"Node\", \"\"> =>\n makeProto({\n runtime: \"Node\",\n name: \"\",\n functions: Record.empty(),\n groups: Record.empty(),\n });\n\nexport const makeNodeAt = <const Name_ extends string>(\n name: Name_,\n): GroupSpec<\"Node\", Name_> => {\n validateConfectFunctionIdentifier(name);\n\n return makeProto({\n runtime: \"Node\",\n name,\n functions: Record.empty(),\n groups: Record.empty(),\n });\n};\n\nexport const withName = <const Name_ extends string>(\n name: Name_,\n group: Any,\n): AnyWithProps => {\n validateConfectFunctionIdentifier(name);\n const group_ = group as AnyWithProps;\n\n if (group_.name === name) {\n return group_;\n }\n\n
|
|
1
|
+
{"version":3,"file":"GroupSpec.js","names":[],"sources":["../src/GroupSpec.ts"],"sourcesContent":["import { Predicate, Record } from \"effect\";\nimport type * as FunctionSpec from \"./FunctionSpec\";\nimport type * as RuntimeAndFunctionType from \"./RuntimeAndFunctionType\";\nimport { validateConfectFunctionIdentifier } from \"./internal/utils\";\n\nexport const TypeId = \"@confect/core/GroupSpec\";\nexport type TypeId = typeof TypeId;\n\nexport const isGroupSpec = (u: unknown): u is AnyWithProps =>\n Predicate.hasProperty(u, TypeId);\n\nexport interface GroupSpec<\n Runtime extends RuntimeAndFunctionType.Runtime,\n Name_ extends string,\n Functions_ extends FunctionSpec.AnyWithPropsWithRuntime<Runtime> = never,\n Groups_ extends AnyWithPropsWithRuntime<Runtime> = never,\n> {\n readonly [TypeId]: TypeId;\n readonly runtime: Runtime;\n readonly name: Name_;\n readonly functions: {\n [FunctionName in FunctionSpec.Name<\n FunctionSpec.AnyWithPropsWithRuntime<Runtime>\n >]: FunctionSpec.WithName<Functions_, FunctionName>;\n };\n readonly groups: {\n [GroupName in Name<Groups_>]: WithName<Groups_, GroupName>;\n };\n\n addFunction<Function extends FunctionSpec.AnyWithPropsWithRuntime<Runtime>>(\n function_: Function,\n ): GroupSpec<Runtime, Name_, Functions_ | Function, Groups_>;\n\n addGroup<Group extends AnyWithPropsWithRuntime<Runtime>>(\n group: Group,\n ): GroupSpec<Runtime, Name_, Functions_, Groups_ | Group>;\n\n addGroupAt<\n const AtName extends string,\n Group extends AnyWithPropsWithRuntime<Runtime>,\n >(\n name: AtName,\n group: Group,\n ): GroupSpec<Runtime, Name_, Functions_, Groups_ | NamedAt<Group, AtName>>;\n}\n\nexport interface Any {\n readonly [TypeId]: TypeId;\n}\n\nexport interface AnyWithProps extends GroupSpec<\n RuntimeAndFunctionType.Runtime,\n string,\n FunctionSpec.AnyWithProps,\n AnyWithProps\n> {}\n\nexport interface AnyWithPropsWithRuntime<\n Runtime extends RuntimeAndFunctionType.Runtime,\n> extends GroupSpec<\n Runtime,\n string,\n FunctionSpec.AnyWithPropsWithRuntime<Runtime>,\n AnyWithPropsWithRuntime<Runtime>\n> {}\n\nexport type Name<Group extends AnyWithProps> = Group[\"name\"];\n\nexport type Functions<Group extends AnyWithProps> =\n Group[\"functions\"][keyof Group[\"functions\"]];\n\nexport type Groups<Group extends AnyWithProps> =\n Group[\"groups\"][keyof Group[\"groups\"]];\n\nexport type GroupNames<Group extends AnyWithProps> = [Groups<Group>] extends [\n never,\n]\n ? never\n : Name<Groups<Group>>;\n\nexport type WithName<\n Group extends AnyWithProps,\n Name_ extends Name<Group>,\n> = Extract<Group, { readonly name: Name_ }>;\n\n/** Assigns a segment name to a leaf group created with {@link make} for typing and refs. */\nexport type NamedAt<Group extends Any, Name_ extends string> = Omit<\n Group,\n \"name\"\n> & {\n readonly name: Name_;\n};\n\nconst Proto = {\n [TypeId]: TypeId,\n\n addFunction<Function extends FunctionSpec.AnyWithProps>(\n this: Any,\n function_: Function,\n ) {\n const this_ = this as AnyWithProps;\n\n return makeProto({\n runtime: this_.runtime,\n name: this_.name,\n functions: Record.set(this_.functions, function_.name, function_),\n groups: this_.groups,\n });\n },\n\n addGroup<Group extends Any>(this: Any, group: Group) {\n const this_ = this as AnyWithProps;\n const group_ = group as unknown as AnyWithProps;\n\n return makeProto({\n runtime: this_.runtime,\n name: this_.name,\n functions: this_.functions,\n groups: Record.set(this_.groups, group_.name, group_),\n });\n },\n\n addGroupAt<Group extends Any>(this: Any, name: string, group: Group) {\n const this_ = this as AnyWithProps;\n const group_ = group as unknown as AnyWithProps;\n\n return makeProto({\n runtime: this_.runtime,\n name: this_.name,\n functions: this_.functions,\n groups: Record.set(this_.groups, name, withName(name, group_)),\n });\n },\n};\n\nconst makeProto = <\n Runtime extends RuntimeAndFunctionType.Runtime,\n Name_ extends string,\n Functions_ extends FunctionSpec.AnyWithPropsWithRuntime<Runtime>,\n Groups_ extends AnyWithPropsWithRuntime<Runtime>,\n>({\n runtime,\n name,\n functions,\n groups,\n}: {\n runtime: Runtime;\n name: Name_;\n functions: Record.ReadonlyRecord<string, Functions_>;\n groups: Record.ReadonlyRecord<string, Groups_>;\n}): GroupSpec<Runtime, Name_, Functions_, Groups_> =>\n Object.assign(Object.create(Proto), {\n runtime,\n name,\n functions,\n groups,\n });\n\nexport const make = (): GroupSpec<\"Convex\", \"\"> =>\n makeProto({\n runtime: \"Convex\",\n name: \"\",\n functions: Record.empty(),\n groups: Record.empty(),\n });\n\nexport const makeAt = <const Name_ extends string>(\n name: Name_,\n): GroupSpec<\"Convex\", Name_> => {\n validateConfectFunctionIdentifier(name);\n\n return makeProto({\n runtime: \"Convex\",\n name,\n functions: Record.empty(),\n groups: Record.empty(),\n });\n};\n\nexport const makeNode = (): GroupSpec<\"Node\", \"\"> =>\n makeProto({\n runtime: \"Node\",\n name: \"\",\n functions: Record.empty(),\n groups: Record.empty(),\n });\n\nexport const makeNodeAt = <const Name_ extends string>(\n name: Name_,\n): GroupSpec<\"Node\", Name_> => {\n validateConfectFunctionIdentifier(name);\n\n return makeProto({\n runtime: \"Node\",\n name,\n functions: Record.empty(),\n groups: Record.empty(),\n });\n};\n\nexport const withName = <const Name_ extends string>(\n name: Name_,\n group: Any,\n): AnyWithProps => {\n validateConfectFunctionIdentifier(name);\n const group_ = group as AnyWithProps;\n\n if (group_.name === name) {\n return group_;\n }\n\n return makeProto({\n runtime: group_.runtime,\n name,\n functions: group_.functions,\n groups: group_.groups,\n });\n};\n"],"mappings":";;;;;;;;;;;;;;AAKA,MAAa,SAAS;AAGtB,MAAa,eAAe,MAC1B,UAAU,YAAY,GAAG,OAAO;AAoFlC,MAAM,QAAQ;EACX,SAAS;CAEV,YAEE,WACA;EACA,MAAM,QAAQ;AAEd,SAAO,UAAU;GACf,SAAS,MAAM;GACf,MAAM,MAAM;GACZ,WAAW,OAAO,IAAI,MAAM,WAAW,UAAU,MAAM,UAAU;GACjE,QAAQ,MAAM;GACf,CAAC;;CAGJ,SAAuC,OAAc;EACnD,MAAM,QAAQ;EACd,MAAM,SAAS;AAEf,SAAO,UAAU;GACf,SAAS,MAAM;GACf,MAAM,MAAM;GACZ,WAAW,MAAM;GACjB,QAAQ,OAAO,IAAI,MAAM,QAAQ,OAAO,MAAM,OAAO;GACtD,CAAC;;CAGJ,WAAyC,MAAc,OAAc;EACnE,MAAM,QAAQ;EACd,MAAM,SAAS;AAEf,SAAO,UAAU;GACf,SAAS,MAAM;GACf,MAAM,MAAM;GACZ,WAAW,MAAM;GACjB,QAAQ,OAAO,IAAI,MAAM,QAAQ,MAAM,SAAS,MAAM,OAAO,CAAC;GAC/D,CAAC;;CAEL;AAED,MAAM,aAKJ,EACA,SACA,MACA,WACA,aAOA,OAAO,OAAO,OAAO,OAAO,MAAM,EAAE;CAClC;CACA;CACA;CACA;CACD,CAAC;AAEJ,MAAa,aACX,UAAU;CACR,SAAS;CACT,MAAM;CACN,WAAW,OAAO,OAAO;CACzB,QAAQ,OAAO,OAAO;CACvB,CAAC;AAEJ,MAAa,UACX,SAC+B;AAC/B,mCAAkC,KAAK;AAEvC,QAAO,UAAU;EACf,SAAS;EACT;EACA,WAAW,OAAO,OAAO;EACzB,QAAQ,OAAO,OAAO;EACvB,CAAC;;AAGJ,MAAa,iBACX,UAAU;CACR,SAAS;CACT,MAAM;CACN,WAAW,OAAO,OAAO;CACzB,QAAQ,OAAO,OAAO;CACvB,CAAC;AAEJ,MAAa,cACX,SAC6B;AAC7B,mCAAkC,KAAK;AAEvC,QAAO,UAAU;EACf,SAAS;EACT;EACA,WAAW,OAAO,OAAO;EACzB,QAAQ,OAAO,OAAO;EACvB,CAAC;;AAGJ,MAAa,YACX,MACA,UACiB;AACjB,mCAAkC,KAAK;CACvC,MAAM,SAAS;AAEf,KAAI,OAAO,SAAS,KAClB,QAAO;AAGT,QAAO,UAAU;EACf,SAAS,OAAO;EAChB;EACA,WAAW,OAAO;EAClB,QAAQ,OAAO;EAChB,CAAC"}
|
package/dist/Spec.d.ts
CHANGED
|
@@ -10,25 +10,45 @@ type TypeId = typeof TypeId;
|
|
|
10
10
|
declare const isSpec: (u: unknown) => u is AnyWithProps;
|
|
11
11
|
declare const isConvexSpec: (u: unknown) => u is AnyWithPropsWithRuntime<"Convex">;
|
|
12
12
|
declare const isNodeSpec: (u: unknown) => u is AnyWithPropsWithRuntime<"Node">;
|
|
13
|
-
interface Spec<Runtime$
|
|
13
|
+
interface Spec<Runtime$1 extends Runtime, Groups_ extends AnyWithPropsWithRuntime$1<Runtime$1> = never> {
|
|
14
14
|
readonly [TypeId]: TypeId;
|
|
15
|
-
readonly runtime: Runtime$
|
|
15
|
+
readonly runtime: Runtime$1;
|
|
16
16
|
readonly groups: { [GroupName in Name<Groups_>]: WithName<Groups_, GroupName> };
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Mapping from an imported leaf `GroupSpec` reference to its full dot-path
|
|
19
|
+
* within this spec tree. Populated by codegen-emitted `_generated/spec.ts`
|
|
20
|
+
* via {@link Spec#addPath}; consumed by `FunctionImpl.make` /
|
|
21
|
+
* `GroupImpl.make` to resolve a spec's location without walking the tree.
|
|
22
|
+
*/
|
|
23
|
+
readonly paths: ReadonlyMap<AnyWithProps$1, string>;
|
|
24
|
+
add<Group extends AnyWithPropsWithRuntime$1<Runtime$1>>(group: Group): Spec<Runtime$1, Groups_ | Group>;
|
|
25
|
+
addAt<const Name$1 extends string, Group extends AnyWithPropsWithRuntime$1<Runtime$1>>(name: Name$1, group: Group): Spec<Runtime$1, Groups_ | NamedAt<Group, Name$1>>;
|
|
26
|
+
/**
|
|
27
|
+
* Register the imported leaf `group` at `path` within this spec's path
|
|
28
|
+
* mapping. Returns a new `Spec` with one additional entry. The tree shape
|
|
29
|
+
* (`groups`) is unaffected — registration and tree assembly are
|
|
30
|
+
* independent steps, both performed by codegen in `_generated/spec.ts`.
|
|
31
|
+
*
|
|
32
|
+
* Re-registering the same group with the same path is a no-op (cheap
|
|
33
|
+
* defense for codegen watch-mode re-imports). Re-registering with a
|
|
34
|
+
* different path throws.
|
|
35
|
+
*/
|
|
36
|
+
addPath(group: AnyWithProps$1, path: string): Spec<Runtime$1, Groups_>;
|
|
19
37
|
}
|
|
20
38
|
interface Any {
|
|
21
39
|
readonly [TypeId]: TypeId;
|
|
22
40
|
}
|
|
23
41
|
interface AnyWithProps extends Spec<Runtime, AnyWithProps$1> {}
|
|
24
|
-
interface AnyWithPropsWithRuntime<Runtime$
|
|
42
|
+
interface AnyWithPropsWithRuntime<Runtime$2 extends Runtime> extends Spec<Runtime$2, AnyWithPropsWithRuntime$1<Runtime$2>> {}
|
|
25
43
|
type Groups<Spec_ extends AnyWithProps> = Spec_["groups"][keyof Spec_["groups"]];
|
|
26
44
|
declare const make: () => Spec<"Convex">;
|
|
27
45
|
declare const makeNode: () => Spec<"Node">;
|
|
28
46
|
/**
|
|
29
47
|
* Merges a Convex spec with an optional Node spec for use with `Api.make`.
|
|
30
48
|
* When `nodeSpec` is provided, its groups are merged under a "node" namespace,
|
|
31
|
-
* mirroring the structure used by `Refs.make`.
|
|
49
|
+
* mirroring the structure used by `Refs.make`. The node spec's `paths`
|
|
50
|
+
* entries are re-prefixed with `"node."` so they continue to identify the
|
|
51
|
+
* same leaves at their new positions in the merged tree.
|
|
32
52
|
*/
|
|
33
53
|
declare const merge: <ConvexSpec extends AnyWithPropsWithRuntime<"Convex">, NodeSpec extends AnyWithPropsWithRuntime<"Node">>(convexSpec: ConvexSpec, nodeSpec?: NodeSpec) => AnyWithProps;
|
|
34
54
|
//#endregion
|
package/dist/Spec.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Spec.d.ts","names":[],"sources":["../src/Spec.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"Spec.d.ts","names":[],"sources":["../src/Spec.ts"],"mappings":";;;;;;;cAKa,MAAA;AAAA,KACD,MAAA,UAAgB,MAAA;AAAA,cAEf,MAAA,GAAU,CAAA,cAAa,CAAA,IAAK,YAAA;AAAA,cAG5B,YAAA,GACX,CAAA,cACC,CAAA,IAAK,uBAAA;AAAA,cAKK,UAAA,GAAc,CAAA,cAAa,CAAA,IAAK,uBAAA;AAAA,UAK5B,IAAA,mBACC,OAAA,kBACA,yBAAA,CAAkC,SAAA;EAAA,UAExC,MAAA,GAAS,MAAA;EAAA,SACV,OAAA,EAAS,SAAA;EAAA,SACT,MAAA,kBACO,IAAA,CAAe,OAAA,IAAW,QAAA,CACtC,OAAA,EACA,SAAA;;;;;;;WASK,KAAA,EAAO,WAAA,CAAY,cAAA;EAE5B,GAAA,eAAkB,yBAAA,CAAkC,SAAA,GAClD,KAAA,EAAO,KAAA,GACN,IAAA,CAAK,SAAA,EAAS,OAAA,GAAU,KAAA;EAE3B,KAAA,4CAEgB,yBAAA,CAAkC,SAAA,GAEhD,IAAA,EAAM,MAAA,EACN,KAAA,EAAO,KAAA,GACN,IAAA,CAAK,SAAA,EAAS,OAAA,GAAU,OAAA,CAAkB,KAAA,EAAO,MAAA;EAhDnC;;AACnB;;;;;AAEA;;;EAyDE,OAAA,CAAQ,KAAA,EAAO,cAAA,EAAwB,IAAA,WAAe,IAAA,CAAK,SAAA,EAAS,OAAA;AAAA;AAAA,UAGrD,GAAA;EAAA,UACL,MAAA,GAAS,MAAA;AAAA;AAAA,UAGJ,YAAA,SAAqB,IAAA,CACpC,OAAA,EACA,cAAA;AAAA,UAGe,uBAAA,mBACC,OAAA,UACR,IAAA,CAAK,SAAA,EAAS,yBAAA,CAAkC,SAAA;AAAA,KAE9C,MAAA,eAAqB,YAAA,IAC/B,KAAA,iBAAsB,KAAA;AAAA,cAwFX,IAAA,QAAW,IAAA;AAAA,cAGX,QAAA,QAAe,IAAA;;;;AA3J5B;;;;cAqKa,KAAA,sBACQ,uBAAA,6BACF,uBAAA,UAEjB,UAAA,EAAY,UAAA,EACZ,QAAA,GAAW,QAAA,KACV,YAAA"}
|
package/dist/Spec.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { validateConfectFunctionIdentifier } from "./internal/utils.js";
|
|
2
3
|
import { makeNodeAt, withName } from "./GroupSpec.js";
|
|
3
|
-
import { Array, Option, Predicate, Record } from "effect";
|
|
4
|
+
import { Array, Option, Predicate, Record, String } from "effect";
|
|
4
5
|
|
|
5
6
|
//#region src/Spec.ts
|
|
6
7
|
var Spec_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -16,37 +17,68 @@ const TypeId = "@confect/core/Spec";
|
|
|
16
17
|
const isSpec = (u) => Predicate.hasProperty(u, TypeId);
|
|
17
18
|
const isConvexSpec = (u) => Predicate.hasProperty(u, TypeId) && Predicate.hasProperty(u, "runtime") && u.runtime === "Convex";
|
|
18
19
|
const isNodeSpec = (u) => Predicate.hasProperty(u, TypeId) && Predicate.hasProperty(u, "runtime") && u.runtime === "Node";
|
|
20
|
+
const validatePath = (path) => {
|
|
21
|
+
if (path.length === 0) throw new Error("Expected a non-empty Confect group path, but received an empty string.");
|
|
22
|
+
const segments = String.split(path, ".");
|
|
23
|
+
for (const segment of segments) {
|
|
24
|
+
if (segment.length === 0) throw new Error(`Expected a Confect group path made of dot-separated identifier segments, but received: "${path}".`);
|
|
25
|
+
validateConfectFunctionIdentifier(segment);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
19
28
|
const Proto = {
|
|
20
29
|
[TypeId]: TypeId,
|
|
21
30
|
add(group) {
|
|
22
31
|
return makeProto({
|
|
23
32
|
runtime: this.runtime,
|
|
24
|
-
groups: Record.set(this.groups, group.name, group)
|
|
33
|
+
groups: Record.set(this.groups, group.name, group),
|
|
34
|
+
paths: this.paths
|
|
25
35
|
});
|
|
26
36
|
},
|
|
27
37
|
addAt(name, group) {
|
|
28
38
|
return makeProto({
|
|
29
39
|
runtime: this.runtime,
|
|
30
|
-
groups: Record.set(this.groups, name, withName(name, group))
|
|
40
|
+
groups: Record.set(this.groups, name, withName(name, group)),
|
|
41
|
+
paths: this.paths
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
addPath(group, path) {
|
|
45
|
+
validatePath(path);
|
|
46
|
+
const existing = this.paths.get(group);
|
|
47
|
+
if (existing !== void 0) {
|
|
48
|
+
if (existing === path) return this;
|
|
49
|
+
throw new Error(`Spec.addPath: the provided GroupSpec is already registered at "${existing}", but was re-registered at "${path}". Each GroupSpec must have at most one path.`);
|
|
50
|
+
}
|
|
51
|
+
const nextPaths = new Map(this.paths);
|
|
52
|
+
nextPaths.set(group, path);
|
|
53
|
+
return makeProto({
|
|
54
|
+
runtime: this.runtime,
|
|
55
|
+
groups: this.groups,
|
|
56
|
+
paths: nextPaths
|
|
31
57
|
});
|
|
32
58
|
}
|
|
33
59
|
};
|
|
34
|
-
const makeProto = ({ runtime, groups }) => Object.assign(Object.create(Proto), {
|
|
60
|
+
const makeProto = ({ runtime, groups, paths }) => Object.assign(Object.create(Proto), {
|
|
35
61
|
runtime,
|
|
36
|
-
groups
|
|
62
|
+
groups,
|
|
63
|
+
paths
|
|
37
64
|
});
|
|
65
|
+
const emptyPaths = () => /* @__PURE__ */ new Map();
|
|
38
66
|
const make = () => makeProto({
|
|
39
67
|
runtime: "Convex",
|
|
40
|
-
groups: {}
|
|
68
|
+
groups: {},
|
|
69
|
+
paths: emptyPaths()
|
|
41
70
|
});
|
|
42
71
|
const makeNode = () => makeProto({
|
|
43
72
|
runtime: "Node",
|
|
44
|
-
groups: {}
|
|
73
|
+
groups: {},
|
|
74
|
+
paths: emptyPaths()
|
|
45
75
|
});
|
|
46
76
|
/**
|
|
47
77
|
* Merges a Convex spec with an optional Node spec for use with `Api.make`.
|
|
48
78
|
* When `nodeSpec` is provided, its groups are merged under a "node" namespace,
|
|
49
|
-
* mirroring the structure used by `Refs.make`.
|
|
79
|
+
* mirroring the structure used by `Refs.make`. The node spec's `paths`
|
|
80
|
+
* entries are re-prefixed with `"node."` so they continue to identify the
|
|
81
|
+
* same leaves at their new positions in the merged tree.
|
|
50
82
|
*/
|
|
51
83
|
const merge = (convexSpec, nodeSpec) => {
|
|
52
84
|
const groups = Option.fromNullable(nodeSpec).pipe(Option.map((nodeSpec_) => Array.reduce(Record.toEntries(nodeSpec_.groups), makeNodeAt("node"), (nodeGroupSpec, [name, group]) => nodeGroupSpec.addGroupAt(name, group))), Option.match({
|
|
@@ -56,9 +88,12 @@ const merge = (convexSpec, nodeSpec) => {
|
|
|
56
88
|
node: nodeGroup
|
|
57
89
|
})
|
|
58
90
|
}));
|
|
91
|
+
const paths = new Map(convexSpec.paths);
|
|
92
|
+
if (nodeSpec !== void 0) for (const [group, path] of nodeSpec.paths) paths.set(group, `node.${path}`);
|
|
59
93
|
return Object.assign(Object.create(Proto), {
|
|
60
94
|
runtime: "Convex",
|
|
61
|
-
groups
|
|
95
|
+
groups,
|
|
96
|
+
paths
|
|
62
97
|
});
|
|
63
98
|
};
|
|
64
99
|
|
package/dist/Spec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Spec.js","names":["GroupSpec.withName","GroupSpec.makeNodeAt"],"sources":["../src/Spec.ts"],"sourcesContent":["import { Array, Option, Predicate, Record } from \"effect\";\nimport * as GroupSpec from \"./GroupSpec\";\nimport type * as RuntimeAndFunctionType from \"./RuntimeAndFunctionType\";\n\nexport const TypeId = \"@confect/core/Spec\";\nexport type TypeId = typeof TypeId;\n\nexport const isSpec = (u: unknown): u is AnyWithProps =>\n Predicate.hasProperty(u, TypeId);\n\nexport const isConvexSpec = (\n u: unknown,\n): u is AnyWithPropsWithRuntime<\"Convex\"> =>\n Predicate.hasProperty(u, TypeId) &&\n Predicate.hasProperty(u, \"runtime\") &&\n u.runtime === \"Convex\";\n\nexport const isNodeSpec = (u: unknown): u is AnyWithPropsWithRuntime<\"Node\"> =>\n Predicate.hasProperty(u, TypeId) &&\n Predicate.hasProperty(u, \"runtime\") &&\n u.runtime === \"Node\";\n\nexport interface Spec<\n Runtime extends RuntimeAndFunctionType.Runtime,\n Groups_ extends GroupSpec.AnyWithPropsWithRuntime<Runtime> = never,\n> {\n readonly [TypeId]: TypeId;\n readonly runtime: Runtime;\n readonly groups: {\n [GroupName in GroupSpec.Name<Groups_>]: GroupSpec.WithName<\n Groups_,\n GroupName\n >;\n };\n\n add<Group extends GroupSpec.AnyWithPropsWithRuntime<Runtime>>(\n group: Group,\n ): Spec<Runtime, Groups_ | Group>;\n\n addAt<\n const Name extends string,\n Group extends GroupSpec.AnyWithPropsWithRuntime<Runtime>,\n >(\n name: Name,\n group: Group,\n ): Spec<Runtime, Groups_ | GroupSpec.NamedAt<Group, Name>>;\n}\n\nexport interface Any {\n readonly [TypeId]: TypeId;\n}\n\nexport interface AnyWithProps extends Spec<\n RuntimeAndFunctionType.Runtime,\n GroupSpec.AnyWithProps\n> {}\n\nexport interface AnyWithPropsWithRuntime<\n Runtime extends RuntimeAndFunctionType.Runtime,\n> extends Spec<Runtime, GroupSpec.AnyWithPropsWithRuntime<Runtime>> {}\n\nexport type Groups<Spec_ extends AnyWithProps> =\n Spec_[\"groups\"][keyof Spec_[\"groups\"]];\n\nconst Proto = {\n [TypeId]: TypeId,\n\n add<Group extends GroupSpec.AnyWithProps>(this: AnyWithProps, group: Group) {\n return makeProto({\n runtime: this.runtime,\n groups: Record.set(this.groups, group.name, group),\n });\n },\n\n addAt<Group extends GroupSpec.AnyWithProps>(\n this: AnyWithProps,\n name: string,\n group: Group,\n ) {\n return makeProto({\n runtime: this.runtime,\n groups: Record.set(this.groups, name, GroupSpec.withName(name, group)),\n });\n },\n};\n\nconst makeProto = <\n Runtime extends RuntimeAndFunctionType.Runtime,\n Groups_ extends GroupSpec.AnyWithPropsWithRuntime<Runtime>,\n>({\n runtime,\n groups,\n}: {\n runtime: Runtime;\n groups: Record.ReadonlyRecord<string, Groups_>;\n}): Spec<Runtime, Groups_> =>\n Object.assign(Object.create(Proto), {\n runtime,\n groups,\n });\n\nexport const make = (): Spec<\"Convex\"> =>\n makeProto({ runtime: \"Convex\", groups: {} });\n\nexport const makeNode = (): Spec<\"Node\"> =>\n makeProto({ runtime: \"Node\", groups: {} });\n\n/**\n * Merges a Convex spec with an optional Node spec for use with `Api.make`.\n * When `nodeSpec` is provided, its groups are merged under a \"node\" namespace,\n * mirroring the structure used by `Refs.make
|
|
1
|
+
{"version":3,"file":"Spec.js","names":["GroupSpec.withName","GroupSpec.makeNodeAt"],"sources":["../src/Spec.ts"],"sourcesContent":["import { Array, Option, Predicate, Record, String } from \"effect\";\nimport * as GroupSpec from \"./GroupSpec\";\nimport type * as RuntimeAndFunctionType from \"./RuntimeAndFunctionType\";\nimport { validateConfectFunctionIdentifier } from \"./internal/utils\";\n\nexport const TypeId = \"@confect/core/Spec\";\nexport type TypeId = typeof TypeId;\n\nexport const isSpec = (u: unknown): u is AnyWithProps =>\n Predicate.hasProperty(u, TypeId);\n\nexport const isConvexSpec = (\n u: unknown,\n): u is AnyWithPropsWithRuntime<\"Convex\"> =>\n Predicate.hasProperty(u, TypeId) &&\n Predicate.hasProperty(u, \"runtime\") &&\n u.runtime === \"Convex\";\n\nexport const isNodeSpec = (u: unknown): u is AnyWithPropsWithRuntime<\"Node\"> =>\n Predicate.hasProperty(u, TypeId) &&\n Predicate.hasProperty(u, \"runtime\") &&\n u.runtime === \"Node\";\n\nexport interface Spec<\n Runtime extends RuntimeAndFunctionType.Runtime,\n Groups_ extends GroupSpec.AnyWithPropsWithRuntime<Runtime> = never,\n> {\n readonly [TypeId]: TypeId;\n readonly runtime: Runtime;\n readonly groups: {\n [GroupName in GroupSpec.Name<Groups_>]: GroupSpec.WithName<\n Groups_,\n GroupName\n >;\n };\n /**\n * Mapping from an imported leaf `GroupSpec` reference to its full dot-path\n * within this spec tree. Populated by codegen-emitted `_generated/spec.ts`\n * via {@link Spec#addPath}; consumed by `FunctionImpl.make` /\n * `GroupImpl.make` to resolve a spec's location without walking the tree.\n */\n readonly paths: ReadonlyMap<GroupSpec.AnyWithProps, string>;\n\n add<Group extends GroupSpec.AnyWithPropsWithRuntime<Runtime>>(\n group: Group,\n ): Spec<Runtime, Groups_ | Group>;\n\n addAt<\n const Name extends string,\n Group extends GroupSpec.AnyWithPropsWithRuntime<Runtime>,\n >(\n name: Name,\n group: Group,\n ): Spec<Runtime, Groups_ | GroupSpec.NamedAt<Group, Name>>;\n\n /**\n * Register the imported leaf `group` at `path` within this spec's path\n * mapping. Returns a new `Spec` with one additional entry. The tree shape\n * (`groups`) is unaffected — registration and tree assembly are\n * independent steps, both performed by codegen in `_generated/spec.ts`.\n *\n * Re-registering the same group with the same path is a no-op (cheap\n * defense for codegen watch-mode re-imports). Re-registering with a\n * different path throws.\n */\n addPath(group: GroupSpec.AnyWithProps, path: string): Spec<Runtime, Groups_>;\n}\n\nexport interface Any {\n readonly [TypeId]: TypeId;\n}\n\nexport interface AnyWithProps extends Spec<\n RuntimeAndFunctionType.Runtime,\n GroupSpec.AnyWithProps\n> {}\n\nexport interface AnyWithPropsWithRuntime<\n Runtime extends RuntimeAndFunctionType.Runtime,\n> extends Spec<Runtime, GroupSpec.AnyWithPropsWithRuntime<Runtime>> {}\n\nexport type Groups<Spec_ extends AnyWithProps> =\n Spec_[\"groups\"][keyof Spec_[\"groups\"]];\n\nconst validatePath = (path: string): void => {\n if (path.length === 0) {\n throw new Error(\n \"Expected a non-empty Confect group path, but received an empty string.\",\n );\n }\n\n const segments = String.split(path, \".\");\n\n for (const segment of segments) {\n if (segment.length === 0) {\n throw new Error(\n `Expected a Confect group path made of dot-separated identifier segments, but received: \"${path}\".`,\n );\n }\n validateConfectFunctionIdentifier(segment);\n }\n};\n\nconst Proto = {\n [TypeId]: TypeId,\n\n add<Group extends GroupSpec.AnyWithProps>(this: AnyWithProps, group: Group) {\n return makeProto({\n runtime: this.runtime,\n groups: Record.set(this.groups, group.name, group),\n paths: this.paths,\n });\n },\n\n addAt<Group extends GroupSpec.AnyWithProps>(\n this: AnyWithProps,\n name: string,\n group: Group,\n ) {\n return makeProto({\n runtime: this.runtime,\n groups: Record.set(this.groups, name, GroupSpec.withName(name, group)),\n paths: this.paths,\n });\n },\n\n addPath(this: AnyWithProps, group: GroupSpec.AnyWithProps, path: string) {\n validatePath(path);\n\n const existing = this.paths.get(group);\n if (existing !== undefined) {\n if (existing === path) {\n return this;\n }\n throw new Error(\n `Spec.addPath: the provided GroupSpec is already registered at \"${existing}\", but was re-registered at \"${path}\". Each GroupSpec must have at most one path.`,\n );\n }\n\n const nextPaths = new Map(this.paths);\n nextPaths.set(group, path);\n\n return makeProto({\n runtime: this.runtime,\n groups: this.groups,\n paths: nextPaths,\n });\n },\n};\n\nconst makeProto = <\n Runtime extends RuntimeAndFunctionType.Runtime,\n Groups_ extends GroupSpec.AnyWithPropsWithRuntime<Runtime>,\n>({\n runtime,\n groups,\n paths,\n}: {\n runtime: Runtime;\n groups: Record.ReadonlyRecord<string, Groups_>;\n paths: ReadonlyMap<GroupSpec.AnyWithProps, string>;\n}): Spec<Runtime, Groups_> =>\n Object.assign(Object.create(Proto), {\n runtime,\n groups,\n paths,\n });\n\nconst emptyPaths = (): ReadonlyMap<GroupSpec.AnyWithProps, string> => new Map();\n\nexport const make = (): Spec<\"Convex\"> =>\n makeProto({ runtime: \"Convex\", groups: {}, paths: emptyPaths() });\n\nexport const makeNode = (): Spec<\"Node\"> =>\n makeProto({ runtime: \"Node\", groups: {}, paths: emptyPaths() });\n\n/**\n * Merges a Convex spec with an optional Node spec for use with `Api.make`.\n * When `nodeSpec` is provided, its groups are merged under a \"node\" namespace,\n * mirroring the structure used by `Refs.make`. The node spec's `paths`\n * entries are re-prefixed with `\"node.\"` so they continue to identify the\n * same leaves at their new positions in the merged tree.\n */\nexport const merge = <\n ConvexSpec extends AnyWithPropsWithRuntime<\"Convex\">,\n NodeSpec extends AnyWithPropsWithRuntime<\"Node\">,\n>(\n convexSpec: ConvexSpec,\n nodeSpec?: NodeSpec,\n): AnyWithProps => {\n const groups = Option.fromNullable(nodeSpec).pipe(\n Option.map((nodeSpec_) =>\n Array.reduce(\n Record.toEntries(nodeSpec_.groups),\n GroupSpec.makeNodeAt(\"node\"),\n (nodeGroupSpec, [name, group]) => nodeGroupSpec.addGroupAt(name, group),\n ),\n ),\n Option.match({\n onNone: () => convexSpec.groups,\n onSome: (nodeGroup) => ({ ...convexSpec.groups, node: nodeGroup }),\n }),\n );\n\n const paths = new Map(convexSpec.paths);\n if (nodeSpec !== undefined) {\n for (const [group, path] of nodeSpec.paths) {\n paths.set(group, `node.${path}`);\n }\n }\n\n return Object.assign(Object.create(Proto), {\n runtime: \"Convex\" as const,\n groups,\n paths,\n }) as AnyWithProps;\n};\n"],"mappings":";;;;;;;;;;;;;;;AAKA,MAAa,SAAS;AAGtB,MAAa,UAAU,MACrB,UAAU,YAAY,GAAG,OAAO;AAElC,MAAa,gBACX,MAEA,UAAU,YAAY,GAAG,OAAO,IAChC,UAAU,YAAY,GAAG,UAAU,IACnC,EAAE,YAAY;AAEhB,MAAa,cAAc,MACzB,UAAU,YAAY,GAAG,OAAO,IAChC,UAAU,YAAY,GAAG,UAAU,IACnC,EAAE,YAAY;AA+DhB,MAAM,gBAAgB,SAAuB;AAC3C,KAAI,KAAK,WAAW,EAClB,OAAM,IAAI,MACR,yEACD;CAGH,MAAM,WAAW,OAAO,MAAM,MAAM,IAAI;AAExC,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,QAAQ,WAAW,EACrB,OAAM,IAAI,MACR,2FAA2F,KAAK,IACjG;AAEH,oCAAkC,QAAQ;;;AAI9C,MAAM,QAAQ;EACX,SAAS;CAEV,IAA8D,OAAc;AAC1E,SAAO,UAAU;GACf,SAAS,KAAK;GACd,QAAQ,OAAO,IAAI,KAAK,QAAQ,MAAM,MAAM,MAAM;GAClD,OAAO,KAAK;GACb,CAAC;;CAGJ,MAEE,MACA,OACA;AACA,SAAO,UAAU;GACf,SAAS,KAAK;GACd,QAAQ,OAAO,IAAI,KAAK,QAAQ,MAAMA,SAAmB,MAAM,MAAM,CAAC;GACtE,OAAO,KAAK;GACb,CAAC;;CAGJ,QAA4B,OAA+B,MAAc;AACvE,eAAa,KAAK;EAElB,MAAM,WAAW,KAAK,MAAM,IAAI,MAAM;AACtC,MAAI,aAAa,QAAW;AAC1B,OAAI,aAAa,KACf,QAAO;AAET,SAAM,IAAI,MACR,kEAAkE,SAAS,+BAA+B,KAAK,+CAChH;;EAGH,MAAM,YAAY,IAAI,IAAI,KAAK,MAAM;AACrC,YAAU,IAAI,OAAO,KAAK;AAE1B,SAAO,UAAU;GACf,SAAS,KAAK;GACd,QAAQ,KAAK;GACb,OAAO;GACR,CAAC;;CAEL;AAED,MAAM,aAGJ,EACA,SACA,QACA,YAMA,OAAO,OAAO,OAAO,OAAO,MAAM,EAAE;CAClC;CACA;CACA;CACD,CAAC;AAEJ,MAAM,mCAAgE,IAAI,KAAK;AAE/E,MAAa,aACX,UAAU;CAAE,SAAS;CAAU,QAAQ,EAAE;CAAE,OAAO,YAAY;CAAE,CAAC;AAEnE,MAAa,iBACX,UAAU;CAAE,SAAS;CAAQ,QAAQ,EAAE;CAAE,OAAO,YAAY;CAAE,CAAC;;;;;;;;AASjE,MAAa,SAIX,YACA,aACiB;CACjB,MAAM,SAAS,OAAO,aAAa,SAAS,CAAC,KAC3C,OAAO,KAAK,cACV,MAAM,OACJ,OAAO,UAAU,UAAU,OAAO,EAClCC,WAAqB,OAAO,GAC3B,eAAe,CAAC,MAAM,WAAW,cAAc,WAAW,MAAM,MAAM,CACxE,CACF,EACD,OAAO,MAAM;EACX,cAAc,WAAW;EACzB,SAAS,eAAe;GAAE,GAAG,WAAW;GAAQ,MAAM;GAAW;EAClE,CAAC,CACH;CAED,MAAM,QAAQ,IAAI,IAAI,WAAW,MAAM;AACvC,KAAI,aAAa,OACf,MAAK,MAAM,CAAC,OAAO,SAAS,SAAS,MACnC,OAAM,IAAI,OAAO,QAAQ,OAAO;AAIpC,QAAO,OAAO,OAAO,OAAO,OAAO,MAAM,EAAE;EACzC,SAAS;EACT;EACA;EACD,CAAC"}
|
package/package.json
CHANGED
package/src/GroupSpec.ts
CHANGED
|
@@ -209,7 +209,10 @@ export const withName = <const Name_ extends string>(
|
|
|
209
209
|
return group_;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
212
|
+
return makeProto({
|
|
213
|
+
runtime: group_.runtime,
|
|
214
|
+
name,
|
|
215
|
+
functions: group_.functions,
|
|
216
|
+
groups: group_.groups,
|
|
217
|
+
});
|
|
215
218
|
};
|
package/src/Spec.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Array, Option, Predicate, Record } from "effect";
|
|
1
|
+
import { Array, Option, Predicate, Record, String } from "effect";
|
|
2
2
|
import * as GroupSpec from "./GroupSpec";
|
|
3
3
|
import type * as RuntimeAndFunctionType from "./RuntimeAndFunctionType";
|
|
4
|
+
import { validateConfectFunctionIdentifier } from "./internal/utils";
|
|
4
5
|
|
|
5
6
|
export const TypeId = "@confect/core/Spec";
|
|
6
7
|
export type TypeId = typeof TypeId;
|
|
@@ -32,6 +33,13 @@ export interface Spec<
|
|
|
32
33
|
GroupName
|
|
33
34
|
>;
|
|
34
35
|
};
|
|
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>;
|
|
35
43
|
|
|
36
44
|
add<Group extends GroupSpec.AnyWithPropsWithRuntime<Runtime>>(
|
|
37
45
|
group: Group,
|
|
@@ -44,6 +52,18 @@ export interface Spec<
|
|
|
44
52
|
name: Name,
|
|
45
53
|
group: Group,
|
|
46
54
|
): 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_>;
|
|
47
67
|
}
|
|
48
68
|
|
|
49
69
|
export interface Any {
|
|
@@ -62,6 +82,25 @@ export interface AnyWithPropsWithRuntime<
|
|
|
62
82
|
export type Groups<Spec_ extends AnyWithProps> =
|
|
63
83
|
Spec_["groups"][keyof Spec_["groups"]];
|
|
64
84
|
|
|
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
|
+
|
|
65
104
|
const Proto = {
|
|
66
105
|
[TypeId]: TypeId,
|
|
67
106
|
|
|
@@ -69,6 +108,7 @@ const Proto = {
|
|
|
69
108
|
return makeProto({
|
|
70
109
|
runtime: this.runtime,
|
|
71
110
|
groups: Record.set(this.groups, group.name, group),
|
|
111
|
+
paths: this.paths,
|
|
72
112
|
});
|
|
73
113
|
},
|
|
74
114
|
|
|
@@ -80,6 +120,30 @@ const Proto = {
|
|
|
80
120
|
return makeProto({
|
|
81
121
|
runtime: this.runtime,
|
|
82
122
|
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,
|
|
83
147
|
});
|
|
84
148
|
},
|
|
85
149
|
};
|
|
@@ -90,25 +154,32 @@ const makeProto = <
|
|
|
90
154
|
>({
|
|
91
155
|
runtime,
|
|
92
156
|
groups,
|
|
157
|
+
paths,
|
|
93
158
|
}: {
|
|
94
159
|
runtime: Runtime;
|
|
95
160
|
groups: Record.ReadonlyRecord<string, Groups_>;
|
|
161
|
+
paths: ReadonlyMap<GroupSpec.AnyWithProps, string>;
|
|
96
162
|
}): Spec<Runtime, Groups_> =>
|
|
97
163
|
Object.assign(Object.create(Proto), {
|
|
98
164
|
runtime,
|
|
99
165
|
groups,
|
|
166
|
+
paths,
|
|
100
167
|
});
|
|
101
168
|
|
|
169
|
+
const emptyPaths = (): ReadonlyMap<GroupSpec.AnyWithProps, string> => new Map();
|
|
170
|
+
|
|
102
171
|
export const make = (): Spec<"Convex"> =>
|
|
103
|
-
makeProto({ runtime: "Convex", groups: {} });
|
|
172
|
+
makeProto({ runtime: "Convex", groups: {}, paths: emptyPaths() });
|
|
104
173
|
|
|
105
174
|
export const makeNode = (): Spec<"Node"> =>
|
|
106
|
-
makeProto({ runtime: "Node", groups: {} });
|
|
175
|
+
makeProto({ runtime: "Node", groups: {}, paths: emptyPaths() });
|
|
107
176
|
|
|
108
177
|
/**
|
|
109
178
|
* Merges a Convex spec with an optional Node spec for use with `Api.make`.
|
|
110
179
|
* When `nodeSpec` is provided, its groups are merged under a "node" namespace,
|
|
111
|
-
* mirroring the structure used by `Refs.make`.
|
|
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
183
|
*/
|
|
113
184
|
export const merge = <
|
|
114
185
|
ConvexSpec extends AnyWithPropsWithRuntime<"Convex">,
|
|
@@ -131,8 +202,16 @@ export const merge = <
|
|
|
131
202
|
}),
|
|
132
203
|
);
|
|
133
204
|
|
|
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
|
+
|
|
134
212
|
return Object.assign(Object.create(Proto), {
|
|
135
213
|
runtime: "Convex" as const,
|
|
136
214
|
groups,
|
|
215
|
+
paths,
|
|
137
216
|
}) as AnyWithProps;
|
|
138
217
|
};
|