@shivaedev/service 0.6.1 → 0.7.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.0 - 2026-11-04
4
+
5
+ ### Breaking
6
+
7
+ - TypeScript source exports now use `@shivaedev/source` instead of `source`. Replace `source` with `@shivaedev/source` in Node `--conditions`, TypeScript `customConditions`, and Vite/Vitest `resolve.conditions`. Bare `source` selects built JavaScript; no compatibility alias is provided.
8
+
9
+ ## 0.6.2 - 2026-11-04
10
+
11
+ ### Fixed
12
+
13
+ - Name the ordinary method and every missing service type in undeclared service requirement diagnostics, excluding declared dependencies and caller scope.
14
+
3
15
  ## 0.6.1 - 2026-11-02
4
16
 
5
17
  ### Fixed
package/README.md CHANGED
@@ -7,13 +7,16 @@ Declare an Effect service's dependencies once, initialize its private state, and
7
7
  A service should explain what it needs and what callers can do with it. When every method binds dependencies separately, that explanation is scattered across setup and application code. Put the dependency list, private initialization and public methods in one definition:
8
8
 
9
9
  ```ts
10
+ import { defineService } from "@shivaedev/service/defineService.ts";
11
+ import { greet } from "#greetings/greet.ts";
12
+ import { initializeGreetings } from "#greetings/initialize.ts";
13
+ import { Prefix } from "#Prefix.ts";
14
+
10
15
  const Greetings = defineService({
11
16
  id: "app/Greetings",
12
17
  requires: [Prefix],
13
- initialize: Effect.succeed({ punctuation: "!" }),
14
- methods: (state) => ({
15
- greet: Effect.fn("Greetings.greet")((name: string) => Effect.map(Prefix, (prefix) => `${prefix}${name}${state.punctuation}`)),
16
- }),
18
+ initialize: initializeGreetings,
19
+ methods: (state) => ({ greet: greet(state) }),
17
20
  });
18
21
  ```
19
22
 
@@ -36,22 +39,51 @@ The work has three parts:
36
39
  2. Once per service, define its dependency list, private state and methods.
37
40
  3. In application code, yield the service and call its methods.
38
41
 
39
- ### 1. Define a service and provide its dependencies
42
+ ### 1. Assemble the service from focused files
43
+
44
+ A service file is an inventory: a top-level `defineService` call lists `id`, `requires`, `initialize` and `methods` directly. Keep each initializer and operation in its own focused file and import its named export. Use `Effect.void` when there is no initialization state. The methods arrow returns the operation record directly. Each entry names an imported operation or calls an imported state factory; `genericMethod` may wrap either form.
45
+
46
+ Quality enforces this with `effect/service-definition-assembly`, including adapters and tests. It rejects a configuration hidden behind a variable, spreads or computed configuration members, hidden method records, inline operations, local implementation references and implementation bodies beside the assembly. Keep setup inside the imported initializer or operation factory. Existing Quality span checks apply to the implementations: use names such as `Greetings.greet`, with an operation matching its binding. No second span convention applies to service assemblies.
47
+
48
+ The initializer module exports its Effect:
40
49
 
41
50
  ```ts
42
- import { Context, Effect, Layer } from "effect";
43
- import { defineService } from "@shivaedev/service/defineService.ts";
51
+ import { Effect } from "effect";
44
52
 
45
- class Prefix extends Context.Service<Prefix, string>()("app/Prefix") {}
53
+ export const initializeGreetings = Effect.succeed({ punctuation: "!" });
54
+ ```
55
+
56
+ The operation module exports a state factory; its returned function is the public method:
57
+
58
+ ```ts
59
+ import { Effect } from "effect";
60
+ import { Prefix } from "#Prefix.ts";
61
+
62
+ export function greet(state: { readonly punctuation: string }) {
63
+ return Effect.fn("Greetings.greet")((name: string) => Effect.map(Prefix, (prefix) => `${prefix}${name}${state.punctuation}`));
64
+ }
65
+ ```
66
+
67
+ The service module imports those exports. `Prefix` is a separate native Context service:
68
+
69
+ ```ts
70
+ import { defineService } from "@shivaedev/service/defineService.ts";
71
+ import { greet } from "#greetings/greet.ts";
72
+ import { initializeGreetings } from "#greetings/initialize.ts";
73
+ import { Prefix } from "#Prefix.ts";
46
74
 
47
75
  const Greetings = defineService({
48
76
  id: "app/Greetings",
49
77
  requires: [Prefix],
50
- initialize: Effect.succeed({ punctuation: "!" }),
51
- methods: (state) => ({
52
- greet: Effect.fn("Greetings.greet")((name: string) => Effect.map(Prefix, (prefix) => `${prefix}${name}${state.punctuation}`)),
53
- }),
78
+ initialize: initializeGreetings,
79
+ methods: (state) => ({ greet: greet(state) }),
54
80
  });
81
+ ```
82
+
83
+ Application code composes its Layer and calls the service:
84
+
85
+ ```ts
86
+ import { Effect, Layer } from "effect";
55
87
 
56
88
  const live = Greetings.layer.pipe(Layer.provide(Layer.succeed(Prefix, "Hello ")));
57
89
 
@@ -63,7 +95,7 @@ const greeting = Effect.gen(function* () {
63
95
 
64
96
  The Layer needs `Prefix`. After a caller resolves `Greetings`, `greet` needs no `Prefix` from that caller. Its argument and success type are inferred from the method. An initialization failure fails the Layer build with that typed error and runs finalizers registered before the failure. A method failure reaches the caller as that method's typed error, and the same service instance can handle the next call. A method defect remains a defect.
65
97
 
66
- `initialize` and ordinary methods may require the listed services and `Scope.Scope`. The compiler rejects a definition that reads another service. Scope is supplied by the Layer or method caller, so `Scope.Scope` itself cannot appear in `requires`.
98
+ `initialize` and ordinary methods may require the listed services and `Scope.Scope`. The compiler rejects a definition that reads another service. For an ordinary method, the diagnostic names the method and its missing service types, such as `MethodHasUndeclaredServiceRequirements<"write", Reactivity>`. When a method is missing several services, the diagnostic includes their union; declared services and caller scope are excluded. Scope is supplied by the Layer or method caller, so `Scope.Scope` itself cannot appear in `requires`.
67
99
 
68
100
  Native `Context.Reference` keys can appear in `requires`. Their defaults make them requirement-free in Effect, so they add no Layer dependency, including when the reference's value is a scope.
69
101
 
@@ -75,7 +107,6 @@ For an explicitly annotated `Effect.fn` generator, `ServiceRequirements` express
75
107
 
76
108
  ```ts
77
109
  import { Context, Effect, type Scope } from "effect";
78
- import { defineService } from "@shivaedev/service/defineService.ts";
79
110
  import type { ServiceRequirements } from "@shivaedev/service/ServiceRequirements.ts";
80
111
 
81
112
  class Declared extends Context.Service<Declared, { readonly identity: object }>()("app/Declared") {}
@@ -89,20 +120,11 @@ type Requirements<Success, Failure = never, CallerScope extends Scope.Scope = ne
89
120
  CallerScope
90
121
  >;
91
122
 
92
- const Identity = defineService({
93
- id: "app/Identity",
94
- requires: requirements,
95
- initialize: Effect.fn("Identity.initialize")(function* (): Requirements<{
96
- readonly identity: object;
97
- }> {
98
- return { identity: (yield* Declared).identity };
99
- })(),
100
- methods: (state) => ({
101
- sameIdentity: Effect.fn("Identity.sameIdentity")(function* (): Requirements<boolean> {
102
- return (yield* Declared).identity === state.identity;
103
- }),
104
- }),
105
- });
123
+ export const initializeIdentity = Effect.fn("Identity.initializeIdentity")(function* (): Requirements<{
124
+ readonly identity: object;
125
+ }> {
126
+ return { identity: (yield* Declared).identity };
127
+ })();
106
128
  ```
107
129
 
108
130
  The alias is `Effect.fn.Return<Success, Failure, declared services | CallerScope>`. The last parameter can be `Scope.Scope` when the generator acquires a scoped resource; it cannot add another undeclared service. The annotation describes the implementation's requirements. `defineService` removes declared dependencies from the public methods' requirements.
@@ -111,18 +133,15 @@ The alias is `Effect.fn.Return<Success, Failure, declared services | CallerScope
111
133
 
112
134
  ```ts
113
135
  import { Effect, Ref } from "effect";
114
- import { defineService } from "@shivaedev/service/defineService.ts";
115
136
 
116
- const Resources = defineService({
117
- id: "app/Resources",
118
- requires: [],
119
- initialize: Ref.make(0),
120
- methods: (released) => ({
121
- open: Effect.fn("Resources.open")(() => Effect.acquireRelease(Effect.succeed("resource"), () => Ref.update(released, (count) => count + 1))),
122
- released: Effect.fn("Resources.released")(() => Ref.get(released)),
123
- }),
124
- });
137
+ export function open(released: Ref.Ref<number>) {
138
+ return Effect.fn("Resources.open")(() => Effect.acquireRelease(Effect.succeed("resource"), () => Ref.update(released, (count) => count + 1)));
139
+ }
140
+ ```
125
141
 
142
+ The assembly imports `open`, a `released` state factory and an initializer that returns `Ref.make(0)`. Its methods inventory is `(state) => ({ open: open(state), released: released(state) })`. A caller can then use:
143
+
144
+ ```ts
126
145
  const useResource = Effect.gen(function* () {
127
146
  const resources = yield* Resources;
128
147
  yield* Effect.scoped(resources.open());
@@ -149,17 +168,24 @@ The replacement provides the public method interface, rather than private initia
149
168
  Ordinary methods have one concrete signature. `genericMethod` marks a generic function so its success, failure and caller requirements remain related at each call:
150
169
 
151
170
  ```ts
152
- import { Context, Effect } from "effect";
153
- import { defineService } from "@shivaedev/service/defineService.ts";
154
- import { genericMethod } from "@shivaedev/service/genericMethod.ts";
171
+ import { Effect } from "effect";
155
172
 
156
- class Caller extends Context.Service<Caller, { readonly value: number }>()("app/Caller") {}
157
-
158
- const preserve = Effect.fn("Generic.preserve")(
173
+ export const preserve = Effect.fn("Generic.preserve")(
159
174
  <Success, Failure, Requirements>(
160
175
  effect: Effect.Effect<Success, Failure, Requirements>,
161
176
  ): Effect.Effect<{ readonly value: Success }, Failure, Requirements> => Effect.map(effect, (value) => ({ value })),
162
177
  );
178
+ ```
179
+
180
+ The assembly imports the generic operation and marks it in the inventory:
181
+
182
+ ```ts
183
+ import { Context, Effect } from "effect";
184
+ import { defineService } from "@shivaedev/service/defineService.ts";
185
+ import { genericMethod } from "@shivaedev/service/genericMethod.ts";
186
+ import { preserve } from "#preserve.ts";
187
+
188
+ class Caller extends Context.Service<Caller, { readonly value: number }>()("app/Caller") {}
163
189
 
164
190
  const Generic = defineService({
165
191
  id: "app/Generic",
@@ -167,7 +193,11 @@ const Generic = defineService({
167
193
  initialize: Effect.void,
168
194
  methods: () => ({ preserve: genericMethod(preserve) }),
169
195
  });
196
+ ```
170
197
 
198
+ A caller keeps the generic operation's requirements:
199
+
200
+ ```ts
171
201
  const preserved = Effect.gen(function* () {
172
202
  const service = yield* Generic;
173
203
  return yield* service.preserve(Effect.map(Caller, ({ value }) => value));
@@ -1,23 +1,25 @@
1
1
  import type { Effect, Scope } from "effect";
2
- import type { AnyMethod, GenericMethodDescriptor, GenericOrStructurallyOverloadedMethodsAreUnsupported, HasDistinctCallSignatures } from "./genericMethod.ts";
3
- import type { RequirementRecord, RequirementsOf } from "./ServiceRequirements.ts";
2
+ import type { AnyMethod, GenericMethodDescriptor, GenericOrStructurallyOverloadedMethodsAreUnsupported, HasDistinctCallSignatures } from "#genericMethod.ts";
3
+ import type { RequirementRecord, RequirementsOf } from "#ServiceRequirements.ts";
4
4
  export type MethodEntry = AnyMethod | GenericMethodDescriptor<AnyMethod>;
5
5
  export type MethodRecord = Readonly<Record<string, AnyMethod>>;
6
6
  export type MethodInventory = Readonly<Record<string, unknown>>;
7
7
  export type RuntimeMethodInventory = Readonly<Record<string, MethodEntry>>;
8
8
  type MethodRequirements<TMethod> = TMethod extends (...arguments_: readonly never[]) => Effect.Effect<unknown, unknown, infer TRequirements> ? TRequirements : never;
9
- interface MethodHasUndeclaredServiceRequirements {
9
+ interface MethodHasUndeclaredServiceRequirements<TName extends PropertyKey, TMissing> {
10
10
  readonly _serviceDefinitionError: "method has service requirements absent from the service declaration";
11
+ readonly method: TName;
12
+ readonly missingServices: TMissing;
11
13
  }
12
14
  interface ServiceMembersMustBeMethods {
13
15
  readonly _serviceDefinitionError: "service members must be methods";
14
16
  }
15
- type OrdinaryMethodProof<TMethod, TRequirements extends RequirementRecord> = [
17
+ type OrdinaryMethodProof<TName extends PropertyKey, TMethod, TRequirements extends RequirementRecord> = [
16
18
  Exclude<MethodRequirements<TMethod>, RequirementsOf<TRequirements> | Scope.Scope>
17
- ] extends [never] ? TMethod : MethodHasUndeclaredServiceRequirements;
18
- type SupportedMethod<TMethod, TRequirements extends RequirementRecord> = TMethod extends GenericMethodDescriptor<AnyMethod> ? TMethod : TMethod extends AnyMethod ? HasDistinctCallSignatures<TMethod> extends true ? GenericOrStructurallyOverloadedMethodsAreUnsupported : TMethod extends (...arguments_: infer TArguments) => infer TResult ? ((...arguments_: TArguments) => TResult) extends TMethod ? OrdinaryMethodProof<TMethod, TRequirements> : GenericOrStructurallyOverloadedMethodsAreUnsupported : GenericOrStructurallyOverloadedMethodsAreUnsupported : ServiceMembersMustBeMethods;
19
+ ] extends [never] ? TMethod : MethodHasUndeclaredServiceRequirements<TName, Exclude<MethodRequirements<TMethod>, RequirementsOf<TRequirements> | Scope.Scope>>;
20
+ type SupportedMethod<TName extends PropertyKey, TMethod, TRequirements extends RequirementRecord> = TMethod extends GenericMethodDescriptor<AnyMethod> ? TMethod : TMethod extends AnyMethod ? HasDistinctCallSignatures<TMethod> extends true ? GenericOrStructurallyOverloadedMethodsAreUnsupported : TMethod extends (...arguments_: infer TArguments) => infer TResult ? ((...arguments_: TArguments) => TResult) extends TMethod ? OrdinaryMethodProof<TName, TMethod, TRequirements> : GenericOrStructurallyOverloadedMethodsAreUnsupported : GenericOrStructurallyOverloadedMethodsAreUnsupported : ServiceMembersMustBeMethods;
19
21
  export type MethodProof<TMethods extends MethodInventory, TRequirements extends RequirementRecord> = {
20
- readonly [TName in keyof TMethods]: SupportedMethod<TMethods[TName], TRequirements>;
22
+ readonly [TName in keyof TMethods]: SupportedMethod<TName, TMethods[TName], TRequirements>;
21
23
  };
22
24
  export {};
23
25
  //# sourceMappingURL=MethodProof.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MethodProof.d.ts","sourceRoot":"","sources":["../src/MethodProof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EACX,SAAS,EACT,uBAAuB,EACvB,oDAAoD,EACpD,yBAAyB,EACzB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAElF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;AAEzE,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEhE,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAE3E,KAAK,kBAAkB,CAAC,OAAO,IAAI,OAAO,SAAS,CAAC,GAAG,UAAU,EAAE,SAAS,KAAK,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC,GACzI,aAAa,GACb,KAAK,CAAC;AAET,UAAU,sCAAsC;IAC/C,QAAQ,CAAC,uBAAuB,EAAE,qEAAqE,CAAC;CACxG;AAED,UAAU,2BAA2B;IACpC,QAAQ,CAAC,uBAAuB,EAAE,iCAAiC,CAAC;CACpE;AAED,KAAK,mBAAmB,CAAC,OAAO,EAAE,aAAa,SAAS,iBAAiB,IAAI;IAC5E,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;CACjF,SAAS,CAAC,KAAK,CAAC,GACd,OAAO,GACP,sCAAsC,CAAC;AAE1C,KAAK,eAAe,CAAC,OAAO,EAAE,aAAa,SAAS,iBAAiB,IACpE,OAAO,SAAS,uBAAuB,CAAC,SAAS,CAAC,GAC/C,OAAO,GACP,OAAO,SAAS,SAAS,GACxB,yBAAyB,CAAC,OAAO,CAAC,SAAS,IAAI,GAC9C,oDAAoD,GACpD,OAAO,SAAS,CAAC,GAAG,UAAU,EAAE,MAAM,UAAU,KAAK,MAAM,OAAO,GACjE,CAAC,CAAC,GAAG,UAAU,EAAE,UAAU,KAAK,OAAO,CAAC,SAAS,OAAO,GACvD,mBAAmB,CAAC,OAAO,EAAE,aAAa,CAAC,GAC3C,oDAAoD,GACrD,oDAAoD,GACtD,2BAA2B,CAAC;AAEjC,MAAM,MAAM,WAAW,CAAC,QAAQ,SAAS,eAAe,EAAE,aAAa,SAAS,iBAAiB,IAAI;IACpG,QAAQ,EAAE,KAAK,IAAI,MAAM,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CACnF,CAAC"}
1
+ {"version":3,"file":"MethodProof.d.ts","sourceRoot":"","sources":["../src/MethodProof.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,KAAK,EACX,SAAS,EACT,uBAAuB,EACvB,oDAAoD,EACpD,yBAAyB,EACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEjF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;AAEzE,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAE/D,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEhE,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;AAE3E,KAAK,kBAAkB,CAAC,OAAO,IAAI,OAAO,SAAS,CAAC,GAAG,UAAU,EAAE,SAAS,KAAK,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC,GACzI,aAAa,GACb,KAAK,CAAC;AAET,UAAU,sCAAsC,CAAC,KAAK,SAAS,WAAW,EAAE,QAAQ;IACnF,QAAQ,CAAC,uBAAuB,EAAE,qEAAqE,CAAC;IACxG,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IACvB,QAAQ,CAAC,eAAe,EAAE,QAAQ,CAAC;CACnC;AAED,UAAU,2BAA2B;IACpC,QAAQ,CAAC,uBAAuB,EAAE,iCAAiC,CAAC;CACpE;AAED,KAAK,mBAAmB,CAAC,KAAK,SAAS,WAAW,EAAE,OAAO,EAAE,aAAa,SAAS,iBAAiB,IAAI;IACvG,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;CACjF,SAAS,CAAC,KAAK,CAAC,GACd,OAAO,GACP,sCAAsC,CAAC,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC,aAAa,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AAEpI,KAAK,eAAe,CAAC,KAAK,SAAS,WAAW,EAAE,OAAO,EAAE,aAAa,SAAS,iBAAiB,IAC/F,OAAO,SAAS,uBAAuB,CAAC,SAAS,CAAC,GAC/C,OAAO,GACP,OAAO,SAAS,SAAS,GACxB,yBAAyB,CAAC,OAAO,CAAC,SAAS,IAAI,GAC9C,oDAAoD,GACpD,OAAO,SAAS,CAAC,GAAG,UAAU,EAAE,MAAM,UAAU,KAAK,MAAM,OAAO,GACjE,CAAC,CAAC,GAAG,UAAU,EAAE,UAAU,KAAK,OAAO,CAAC,SAAS,OAAO,GACvD,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,aAAa,CAAC,GAClD,oDAAoD,GACrD,oDAAoD,GACtD,2BAA2B,CAAC;AAEjC,MAAM,MAAM,WAAW,CAAC,QAAQ,SAAS,eAAe,EAAE,aAAa,SAAS,iBAAiB,IAAI;IACpG,QAAQ,EAAE,KAAK,IAAI,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;CAC1F,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/service",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Declared Effect services with scoped initialization and bound dependencies",
5
5
  "keywords": [
6
6
  "effect"
@@ -20,14 +20,14 @@
20
20
  "imports": {
21
21
  "#test/*": "./src/test-support/*",
22
22
  "#*.ts": {
23
- "source": "./src/*.ts",
23
+ "@shivaedev/source": "./src/*.ts",
24
24
  "types": "./dist/*.d.ts",
25
25
  "default": "./dist/*.js"
26
26
  }
27
27
  },
28
28
  "exports": {
29
29
  "./*.ts": {
30
- "source": "./src/*.ts",
30
+ "@shivaedev/source": "./src/*.ts",
31
31
  "types": "./dist/*.d.ts",
32
32
  "default": "./dist/*.js"
33
33
  },
@@ -44,9 +44,9 @@
44
44
  ],
45
45
  "devDependencies": {
46
46
  "@effect/vitest": "4.0.6",
47
- "@shivaedev/quality": "0.55.0",
48
- "@shivaedev/test": "0.12.1",
49
- "@shivaedev/test-story": "0.33.0",
47
+ "@shivaedev/quality": "0.60.0",
48
+ "@shivaedev/test": "0.15.0",
49
+ "@shivaedev/test-story": "0.38.0",
50
50
  "@types/node": "24.19.2",
51
51
  "effect": "4.0.6",
52
52
  "typescript": "7.0.2",
@@ -4,8 +4,8 @@ import type {
4
4
  GenericMethodDescriptor,
5
5
  GenericOrStructurallyOverloadedMethodsAreUnsupported,
6
6
  HasDistinctCallSignatures,
7
- } from "./genericMethod.ts";
8
- import type { RequirementRecord, RequirementsOf } from "./ServiceRequirements.ts";
7
+ } from "#genericMethod.ts";
8
+ import type { RequirementRecord, RequirementsOf } from "#ServiceRequirements.ts";
9
9
 
10
10
  export type MethodEntry = AnyMethod | GenericMethodDescriptor<AnyMethod>;
11
11
 
@@ -19,21 +19,23 @@ type MethodRequirements<TMethod> = TMethod extends (...arguments_: readonly neve
19
19
  ? TRequirements
20
20
  : never;
21
21
 
22
- interface MethodHasUndeclaredServiceRequirements {
22
+ interface MethodHasUndeclaredServiceRequirements<TName extends PropertyKey, TMissing> {
23
23
  readonly _serviceDefinitionError: "method has service requirements absent from the service declaration";
24
+ readonly method: TName;
25
+ readonly missingServices: TMissing;
24
26
  }
25
27
 
26
28
  interface ServiceMembersMustBeMethods {
27
29
  readonly _serviceDefinitionError: "service members must be methods";
28
30
  }
29
31
 
30
- type OrdinaryMethodProof<TMethod, TRequirements extends RequirementRecord> = [
32
+ type OrdinaryMethodProof<TName extends PropertyKey, TMethod, TRequirements extends RequirementRecord> = [
31
33
  Exclude<MethodRequirements<TMethod>, RequirementsOf<TRequirements> | Scope.Scope>,
32
34
  ] extends [never]
33
35
  ? TMethod
34
- : MethodHasUndeclaredServiceRequirements;
36
+ : MethodHasUndeclaredServiceRequirements<TName, Exclude<MethodRequirements<TMethod>, RequirementsOf<TRequirements> | Scope.Scope>>;
35
37
 
36
- type SupportedMethod<TMethod, TRequirements extends RequirementRecord> =
38
+ type SupportedMethod<TName extends PropertyKey, TMethod, TRequirements extends RequirementRecord> =
37
39
  TMethod extends GenericMethodDescriptor<AnyMethod>
38
40
  ? TMethod
39
41
  : TMethod extends AnyMethod
@@ -41,11 +43,11 @@ type SupportedMethod<TMethod, TRequirements extends RequirementRecord> =
41
43
  ? GenericOrStructurallyOverloadedMethodsAreUnsupported
42
44
  : TMethod extends (...arguments_: infer TArguments) => infer TResult
43
45
  ? ((...arguments_: TArguments) => TResult) extends TMethod
44
- ? OrdinaryMethodProof<TMethod, TRequirements>
46
+ ? OrdinaryMethodProof<TName, TMethod, TRequirements>
45
47
  : GenericOrStructurallyOverloadedMethodsAreUnsupported
46
48
  : GenericOrStructurallyOverloadedMethodsAreUnsupported
47
49
  : ServiceMembersMustBeMethods;
48
50
 
49
51
  export type MethodProof<TMethods extends MethodInventory, TRequirements extends RequirementRecord> = {
50
- readonly [TName in keyof TMethods]: SupportedMethod<TMethods[TName], TRequirements>;
52
+ readonly [TName in keyof TMethods]: SupportedMethod<TName, TMethods[TName], TRequirements>;
51
53
  };