navi-di 0.1.0 → 1.0.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/CONTRIBUTING.md CHANGED
@@ -17,11 +17,13 @@ This repository is focused on a small, explicit DI core for standard ECMAScript
17
17
  This repository uses Lefthook.
18
18
 
19
19
  - `pre-commit` runs fast staged-file checks for lint and formatting.
20
+ - hooks are contributor-local and are not installed for package consumers.
20
21
 
21
- If hooks stop working locally, reinstall them with `bun run hooks:install`.
22
+ Install or reinstall them locally with `bun run hooks:install`.
22
23
 
23
24
  ## Pull request checklist
24
25
 
26
+ - PR CI runs lint, format, typecheck, test, and build checks automatically.
25
27
  - the change is focused and documented;
26
28
  - scripts in the local checks section pass;
27
29
  - package exports and Node compatibility were kept intact;
package/README.md CHANGED
@@ -46,11 +46,17 @@ No environment variables or external services are required for local development
46
46
 
47
47
  ## Public entry points
48
48
 
49
- The package root currently exports:
49
+ The package root currently exports runtime values and TypeScript types.
50
+
51
+ Runtime exports:
50
52
 
51
53
  - `Container`
52
54
  - `Service`
53
55
  - `Inject`
56
+ - `Token`
57
+
58
+ Type-only exports:
59
+
54
60
  - `Constructable` / `AbstractConstructable`
55
61
  - `ServiceIdentifier`
56
62
 
@@ -126,10 +132,16 @@ Current behavior:
126
132
 
127
133
  ## Decorators
128
134
 
129
- ### `@Service(options?)`
135
+ ### `@Service(idOrOptions?)`
130
136
 
131
137
  Registers a class in the default container.
132
138
 
139
+ Accepted forms today:
140
+
141
+ - `@Service()`
142
+ - `@Service(id)`
143
+ - `@Service({ id, scope })`
144
+
133
145
  Options supported today:
134
146
 
135
147
  - `id?: ServiceIdentifier`
@@ -146,7 +158,8 @@ class LoggerService {}
146
158
  const logger = Container.of().get('logger');
147
159
  ```
148
160
 
149
- Note: a custom string id works for manual resolution through `container.get(...)`, but `@Inject()` currently accepts a constructable class dependency rather than an arbitrary token.
161
+ Custom identifiers work for both `container.get(...)` and `@Inject(...)`.
162
+ When you use `Token` instances, resolution is based on object identity, so create the token once and reuse the same instance everywhere.
150
163
 
151
164
  ### `@Inject(dependency)`
152
165
 
@@ -161,6 +174,35 @@ Current characteristics:
161
174
  - injected fields are defined as writable and configurable own properties on the created instance;
162
175
  - injected fields are assigned after construction, so they are not available inside constructors or field initializers.
163
176
 
177
+ Token example:
178
+
179
+ ```ts
180
+ import { Container, Inject, Service, Token } from 'navi-di';
181
+
182
+ interface Logger {
183
+ log(message: string): void;
184
+ }
185
+
186
+ const LOGGER = new Token<Logger>('Logger');
187
+
188
+ @Service(LOGGER)
189
+ class ConsoleLogger implements Logger {
190
+ public log(message: string) {
191
+ console.log(message);
192
+ }
193
+ }
194
+
195
+ @Service()
196
+ class HandlerService {
197
+ @Inject(LOGGER)
198
+ public logger!: Logger;
199
+ }
200
+
201
+ const handler = Container.of().get(HandlerService);
202
+
203
+ handler.logger.log('hello from token injection');
204
+ ```
205
+
164
206
  ## Container API
165
207
 
166
208
  ### `Container.of(id?)`
@@ -193,6 +235,13 @@ Supported strategies:
193
235
 
194
236
  This is especially useful in tests.
195
237
 
238
+ ### `container.set(metadata)`
239
+
240
+ Registers or replaces service metadata for a service identifier.
241
+
242
+ This is a low-level API that powers manual registration scenarios and internal tests.
243
+ For application-facing code, prefer `@Service()` unless you specifically need to construct metadata yourself.
244
+
196
245
  ## Internal architecture
197
246
 
198
247
  The implementation is intentionally small and split into a few focused modules:
@@ -242,9 +291,9 @@ What they do:
242
291
  - `lint`: run `oxlint` with warnings denied
243
292
  - `fmt`: format the repository with `oxfmt`
244
293
  - `fmt:check`: verify formatting without writing changes
245
- - `hooks:*`: install, validate, or run the Lefthook-based Git hooks
294
+ - `hooks:*`: install, validate, or run the repo-local Lefthook Git hooks
246
295
 
247
- `bun install` also triggers `postinstall`, which installs the local Git hooks automatically.
296
+ Git hooks are optional and repo-local. After cloning, contributors can install them with `bun run hooks:install`.
248
297
 
249
298
  Current note: `typecheck` uses `tsconfig.json` with `noEmit: true`, while `build` uses `tsconfig.build.json` with `noEmit: false` to emit `dist/` and declaration files.
250
299
 
@@ -255,7 +304,8 @@ The repository currently enforces:
255
304
  - strict TypeScript checking;
256
305
  - `oxlint` for linting;
257
306
  - `oxfmt` for formatting;
258
- - Lefthook `pre-commit` checks for staged TypeScript, JavaScript, Markdown, YAML, and YML files.
307
+ - PR CI checks for lint, format, typecheck, tests, and build;
308
+ - optional Lefthook `pre-commit` checks for staged TypeScript, JavaScript, Markdown, YAML, and YML files.
259
309
 
260
310
  The package `prepack` script runs lint, format check, typecheck, and build before publishing.
261
311
 
@@ -1,2 +1,2 @@
1
- import { type Constructable } from '../types';
2
- export declare function Inject<T>(dependency: Constructable<T>): (_: undefined, context: ClassFieldDecoratorContext) => void;
1
+ import { type ServiceIdentifier } from '../types';
2
+ export declare function Inject<T>(dependency: ServiceIdentifier<T>): (_: undefined, context: ClassFieldDecoratorContext) => void;
@@ -1,3 +1,4 @@
1
- import type { ServiceOption } from '../types';
1
+ import type { ServiceIdentifier, ServiceOption } from '../types';
2
2
  export declare function Service(): Function;
3
+ export declare function Service(id: ServiceIdentifier): Function;
3
4
  export declare function Service(options?: ServiceOption): Function;
@@ -1,7 +1,17 @@
1
1
  import { ContainerRegistry } from '../container/registry';
2
2
  import { INJECTION_KEY, EMPTY_VALUE } from '../types';
3
- export function Service(options) {
3
+ function normalizeArguments(args) {
4
+ if (!args) {
5
+ return {};
6
+ }
7
+ if (typeof args === 'object' && ('scope' in args || 'id' in args)) {
8
+ return args;
9
+ }
10
+ return { id: args };
11
+ }
12
+ export function Service(idOrOptions) {
4
13
  return function (target, context) {
14
+ const options = normalizeArguments(idOrOptions);
5
15
  const injections = (context.metadata[INJECTION_KEY] ?? []);
6
16
  ContainerRegistry.defaultContainer.set({
7
17
  id: options?.id ?? target,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { Container } from './container';
2
2
  export { Service, Inject } from './decorators';
3
+ export { Token } from './tokens';
3
4
  export type { AbstractConstructable, Constructable } from './types/constructable.ts';
4
5
  export type { ServiceIdentifier } from './types/service.ts';
package/dist/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { Container } from './container';
2
2
  export { Service, Inject } from './decorators';
3
+ export { Token } from './tokens';
@@ -0,0 +1 @@
1
+ export { Token } from './token';
@@ -0,0 +1 @@
1
+ export { Token } from './token';
@@ -0,0 +1,5 @@
1
+ export declare class Token<T> {
2
+ name?: string;
3
+ constructor(name?: string);
4
+ toString(): string;
5
+ }
@@ -0,0 +1,10 @@
1
+ // oxlint-disable-next-line no-unused-vars
2
+ export class Token {
3
+ name;
4
+ constructor(name) {
5
+ this.name = name;
6
+ }
7
+ toString() {
8
+ return this.name ? `Token(${this.name})` : `Token()`;
9
+ }
10
+ }
@@ -1,5 +1,6 @@
1
+ import type { Token } from '../tokens';
1
2
  import type { AbstractConstructable, Constructable } from './constructable';
2
- export type ServiceIdentifier<T = unknown, Args extends unknown[] = never[]> = Constructable<T, Args> | AbstractConstructable<T, Args> | CallableFunction | string;
3
+ export type ServiceIdentifier<T = unknown, Args extends unknown[] = never[]> = Constructable<T, Args> | AbstractConstructable<T, Args> | CallableFunction | string | Token<T>;
3
4
  export type ServiceScope = 'singleton' | 'container' | 'transient';
4
5
  export interface ServiceOption {
5
6
  id?: ServiceIdentifier;
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "navi-di",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Dependency injection for standard ECMAScript decorators.",
5
+ "author": "naviary-sanctuary",
5
6
  "keywords": [
6
7
  "bun",
7
8
  "decorators",
@@ -51,7 +52,6 @@
51
52
  "lint": "oxlint . --deny-warnings",
52
53
  "fmt": "oxfmt --config .oxfmt.json .",
53
54
  "fmt:check": "oxfmt --check --config .oxfmt.json .",
54
- "postinstall": "lefthook install",
55
55
  "prepack": "bun run lint && bun run fmt:check && bun run typecheck && bun run build",
56
56
  "test": "bun test --pass-with-no-tests"
57
57
  },