@toyz/loom 0.1.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/LICENSE +21 -0
- package/README.md +108 -0
- package/dist/app.d.ts +70 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +152 -0
- package/dist/app.js.map +1 -0
- package/dist/bus.d.ts +27 -0
- package/dist/bus.d.ts.map +1 -0
- package/dist/bus.js +41 -0
- package/dist/bus.js.map +1 -0
- package/dist/css.d.ts +25 -0
- package/dist/css.d.ts.map +1 -0
- package/dist/css.js +48 -0
- package/dist/css.js.map +1 -0
- package/dist/decorators/component.d.ts +11 -0
- package/dist/decorators/component.d.ts.map +1 -0
- package/dist/decorators/component.js +38 -0
- package/dist/decorators/component.js.map +1 -0
- package/dist/decorators/di.d.ts +37 -0
- package/dist/decorators/di.d.ts.map +1 -0
- package/dist/decorators/di.js +65 -0
- package/dist/decorators/di.js.map +1 -0
- package/dist/decorators/dom.d.ts +15 -0
- package/dist/decorators/dom.d.ts.map +1 -0
- package/dist/decorators/dom.js +33 -0
- package/dist/decorators/dom.js.map +1 -0
- package/dist/decorators/events.d.ts +46 -0
- package/dist/decorators/events.d.ts.map +1 -0
- package/dist/decorators/events.js +69 -0
- package/dist/decorators/events.js.map +1 -0
- package/dist/decorators/index.d.ts +18 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +27 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/decorators/lifecycle.d.ts +49 -0
- package/dist/decorators/lifecycle.d.ts.map +1 -0
- package/dist/decorators/lifecycle.js +105 -0
- package/dist/decorators/lifecycle.js.map +1 -0
- package/dist/decorators/state.d.ts +41 -0
- package/dist/decorators/state.d.ts.map +1 -0
- package/dist/decorators/state.js +125 -0
- package/dist/decorators/state.js.map +1 -0
- package/dist/decorators/symbols.d.ts +13 -0
- package/dist/decorators/symbols.d.ts.map +1 -0
- package/dist/decorators/symbols.js +15 -0
- package/dist/decorators/symbols.js.map +1 -0
- package/dist/decorators/timing.d.ts +35 -0
- package/dist/decorators/timing.d.ts.map +1 -0
- package/dist/decorators/timing.js +57 -0
- package/dist/decorators/timing.js.map +1 -0
- package/dist/decorators/transform.d.ts +45 -0
- package/dist/decorators/transform.d.ts.map +1 -0
- package/dist/decorators/transform.js +48 -0
- package/dist/decorators/transform.js.map +1 -0
- package/dist/element.d.ts +62 -0
- package/dist/element.d.ts.map +1 -0
- package/dist/element.js +150 -0
- package/dist/element.js.map +1 -0
- package/dist/event.d.ts +24 -0
- package/dist/event.d.ts.map +1 -0
- package/dist/event.js +35 -0
- package/dist/event.js.map +1 -0
- package/dist/icon.d.ts +35 -0
- package/dist/icon.d.ts.map +1 -0
- package/dist/icon.js +119 -0
- package/dist/icon.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx-dev-runtime.d.ts +8 -0
- package/dist/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/jsx-dev-runtime.js +8 -0
- package/dist/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx-runtime.d.ts +13 -0
- package/dist/jsx-runtime.d.ts.map +1 -0
- package/dist/jsx-runtime.js +101 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/morph.d.ts +23 -0
- package/dist/morph.d.ts.map +1 -0
- package/dist/morph.js +212 -0
- package/dist/morph.js.map +1 -0
- package/dist/reactive.d.ts +75 -0
- package/dist/reactive.d.ts.map +1 -0
- package/dist/reactive.js +133 -0
- package/dist/reactive.js.map +1 -0
- package/dist/render-loop.d.ts +34 -0
- package/dist/render-loop.d.ts.map +1 -0
- package/dist/render-loop.js +70 -0
- package/dist/render-loop.js.map +1 -0
- package/dist/router/events.d.ts +12 -0
- package/dist/router/events.d.ts.map +1 -0
- package/dist/router/events.js +17 -0
- package/dist/router/events.js.map +1 -0
- package/dist/router/index.d.ts +14 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/index.js +18 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/link.d.ts +18 -0
- package/dist/router/link.d.ts.map +1 -0
- package/dist/router/link.js +75 -0
- package/dist/router/link.js.map +1 -0
- package/dist/router/mode.d.ts +33 -0
- package/dist/router/mode.d.ts.map +1 -0
- package/dist/router/mode.js +48 -0
- package/dist/router/mode.js.map +1 -0
- package/dist/router/outlet.d.ts +40 -0
- package/dist/router/outlet.d.ts.map +1 -0
- package/dist/router/outlet.js +171 -0
- package/dist/router/outlet.js.map +1 -0
- package/dist/router/route.d.ts +76 -0
- package/dist/router/route.d.ts.map +1 -0
- package/dist/router/route.js +147 -0
- package/dist/router/route.js.map +1 -0
- package/dist/router/router.d.ts +50 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/router.js +140 -0
- package/dist/router/router.js.map +1 -0
- package/dist/storage.d.ts +55 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +90 -0
- package/dist/storage.js.map +1 -0
- package/dist/virtual.d.ts +69 -0
- package/dist/virtual.d.ts.map +1 -0
- package/dist/virtual.js +247 -0
- package/dist/virtual.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 toyz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://toyz.github.io/loom/loom-logo.svg" alt="Loom" width="80" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Loom</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Decorator-driven web components that started as a meme and accidentally became useful.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/@toyz/loom"><img src="https://img.shields.io/npm/v/@toyz/loom?color=c084fc&label=npm" alt="npm" /></a>
|
|
13
|
+
<a href="https://toyz.github.io/loom/"><img src="https://img.shields.io/badge/docs-live-86efac" alt="docs" /></a>
|
|
14
|
+
<a href="https://placing.space"><img src="https://img.shields.io/badge/used%20on-placing.space-67e8f9" alt="placing.space" /></a>
|
|
15
|
+
<a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-fbbf24" alt="MIT" /></a>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## What is this?
|
|
21
|
+
|
|
22
|
+
Loom was born out of pure spite for boilerplate. What began as an ironic "what if decorators did _everything_?" experiment turned into a genuinely useful framework for building web components.
|
|
23
|
+
|
|
24
|
+
It powers [placing.space](https://placing.space) in production — a real-time collaborative pixel canvas — so it's been battle-tested with WebSocket streams, thousands of DOM nodes, and zero framework overhead.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- **`@component`** — register custom elements in one line
|
|
29
|
+
- **`@reactive` / `@prop`** — fine-grained reactivity that only re-renders what changed
|
|
30
|
+
- **`@computed` / `@watch`** — derived state and side effects
|
|
31
|
+
- **`@on` / `@emit`** — declarative event handling
|
|
32
|
+
- **JSX + DOM morphing** — write JSX, get surgical DOM patches (no virtual DOM)
|
|
33
|
+
- **`@inject` / `@service`** — dependency injection container
|
|
34
|
+
- **Hash & history router** — `@route`, `@guard`, `<loom-outlet>`
|
|
35
|
+
- **`css\`\``** — adopted stylesheets with zero FOUC
|
|
36
|
+
- **`<loom-virtual>`** — virtualized list for huge datasets
|
|
37
|
+
- **Zero dependencies** — just TypeScript and the platform
|
|
38
|
+
|
|
39
|
+
## Install
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install @toyz/loom
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { LoomElement, component, reactive, css } from "@toyz/loom";
|
|
49
|
+
|
|
50
|
+
const styles = css`
|
|
51
|
+
button { padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; }
|
|
52
|
+
span { font-weight: bold; margin-left: 0.5rem; }
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
@component("click-counter")
|
|
56
|
+
class ClickCounter extends LoomElement {
|
|
57
|
+
@reactive count = 0;
|
|
58
|
+
|
|
59
|
+
update() {
|
|
60
|
+
this.shadow.adoptedStyleSheets = [styles];
|
|
61
|
+
return (
|
|
62
|
+
<button onClick={() => this.count++}>
|
|
63
|
+
Clicks: <span>{this.count}</span>
|
|
64
|
+
</button>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<click-counter></click-counter>
|
|
72
|
+
<script type="module" src="./main.ts"></script>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## TSConfig
|
|
76
|
+
|
|
77
|
+
Loom ships its own JSX runtime. Point your config at it:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"compilerOptions": {
|
|
82
|
+
"jsx": "react-jsx",
|
|
83
|
+
"jsxImportSource": "@toyz/loom"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For Vite:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// vite.config.ts
|
|
92
|
+
export default defineConfig({
|
|
93
|
+
esbuild: {
|
|
94
|
+
jsx: "automatic",
|
|
95
|
+
jsxImportSource: "@toyz/loom",
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Docs
|
|
101
|
+
|
|
102
|
+
Full documentation with interactive examples:
|
|
103
|
+
|
|
104
|
+
**[toyz.github.io/loom](https://toyz.github.io/loom/)**
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
[MIT](./LICENSE) — do whatever you want with it.
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom — Application entry point
|
|
3
|
+
*
|
|
4
|
+
* Boots the render loop, instantiates @service singletons,
|
|
5
|
+
* runs @factory methods, and registers @component custom elements.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { app } from "loom";
|
|
9
|
+
*
|
|
10
|
+
* app
|
|
11
|
+
* .use(natsConnection)
|
|
12
|
+
* .use(chatClient)
|
|
13
|
+
* .start();
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import { type Constructor, type Handler } from "./bus";
|
|
17
|
+
import type { LoomEvent } from "./event";
|
|
18
|
+
declare class LoomApp {
|
|
19
|
+
private providers;
|
|
20
|
+
private services;
|
|
21
|
+
private factories;
|
|
22
|
+
private components;
|
|
23
|
+
private _started;
|
|
24
|
+
/** Subscribe to a typed event. Returns unsubscribe function. */
|
|
25
|
+
on<T>(type: Constructor<T>, handler: Handler<T>): () => void;
|
|
26
|
+
/** Emit a typed event to all listeners. */
|
|
27
|
+
emit<T extends LoomEvent>(event: T): void;
|
|
28
|
+
/** Remove a specific event handler. */
|
|
29
|
+
off<T>(type: Constructor<T>, handler: Handler<T>): void;
|
|
30
|
+
/**
|
|
31
|
+
* Chainable provider registration.
|
|
32
|
+
*
|
|
33
|
+
* app.use(instance) — key auto-inferred from constructor
|
|
34
|
+
* app.use(MyClass) — class constructor, auto-instantiated
|
|
35
|
+
* app.use(() => createThing()) — factory fn, key from result constructor
|
|
36
|
+
* app.use(Key, instance) — explicit key
|
|
37
|
+
*
|
|
38
|
+
* T is always optional — no magic typing forced.
|
|
39
|
+
*/
|
|
40
|
+
use<T = any>(thing: T): this;
|
|
41
|
+
use<T = any>(key: any, instance: T): this;
|
|
42
|
+
/** Queue a @service class for auto-instantiation on start() */
|
|
43
|
+
registerService(ctor: any): void;
|
|
44
|
+
/** Queue a @factory method for invocation on start() */
|
|
45
|
+
registerFactory(key: any, proto: any, method: string): void;
|
|
46
|
+
/** Queue a @component for customElements.define() on start() */
|
|
47
|
+
register(tag: string, ctor: CustomElementConstructor): void;
|
|
48
|
+
/** Retrieve a provider/service by key. Throws if missing. */
|
|
49
|
+
get<T = any>(key: any): T;
|
|
50
|
+
/** Retrieve a provider/service, or undefined if not registered. */
|
|
51
|
+
maybe<T = any>(key: any): T | undefined;
|
|
52
|
+
/** Resolve @inject parameter metadata for a constructor or method. */
|
|
53
|
+
private resolveParams;
|
|
54
|
+
/**
|
|
55
|
+
* Boot the app:
|
|
56
|
+
* 1. Auto-instantiate @service singletons (with constructor @inject)
|
|
57
|
+
* 2. Run @factory methods (with parameter @inject)
|
|
58
|
+
* 3. Start the render loop
|
|
59
|
+
* 4. Register all @component custom elements
|
|
60
|
+
*/
|
|
61
|
+
start(): Promise<void>;
|
|
62
|
+
/** Tear down — stop render loop */
|
|
63
|
+
stop(): void;
|
|
64
|
+
/** Whether the app has been started */
|
|
65
|
+
get started(): boolean;
|
|
66
|
+
}
|
|
67
|
+
/** Module-level singleton — the Loom app instance */
|
|
68
|
+
export declare const app: LoomApp;
|
|
69
|
+
export type { LoomApp };
|
|
70
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,EAAO,KAAK,WAAW,EAAE,KAAK,OAAO,EAAE,MAAM,OAAO,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAQzC,cAAM,OAAO;IACX,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,UAAU,CAAyD;IAC3E,OAAO,CAAC,QAAQ,CAAS;IAIzB,gEAAgE;IAChE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAE5D,2CAA2C;IAC3C,IAAI,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAEzC,uCAAuC;IACvC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAIvD;;;;;;;;;OASG;IACH,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAC5B,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI;IAuBzC,+DAA+D;IAC/D,eAAe,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAIhC,wDAAwD;IACxD,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAI3D,gEAAgE;IAChE,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,wBAAwB,GAAG,IAAI;IAM3D,6DAA6D;IAC7D,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;IAQzB,mEAAmE;IACnE,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,GAAG,SAAS;IAIvC,sEAAsE;IACtE,OAAO,CAAC,aAAa;IAWrB;;;;;;OAMG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA+C5B,mCAAmC;IACnC,IAAI,IAAI,IAAI;IAKZ,uCAAuC;IACvC,IAAI,OAAO,IAAI,OAAO,CAErB;CACF;AAED,qDAAqD;AACrD,eAAO,MAAM,GAAG,SAAgB,CAAC;AACjC,YAAY,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom — Application entry point
|
|
3
|
+
*
|
|
4
|
+
* Boots the render loop, instantiates @service singletons,
|
|
5
|
+
* runs @factory methods, and registers @component custom elements.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { app } from "loom";
|
|
9
|
+
*
|
|
10
|
+
* app
|
|
11
|
+
* .use(natsConnection)
|
|
12
|
+
* .use(chatClient)
|
|
13
|
+
* .start();
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import { renderLoop } from "./render-loop";
|
|
17
|
+
import { INJECT_PARAMS, ON_HANDLERS } from "./decorators";
|
|
18
|
+
import { bus } from "./bus";
|
|
19
|
+
class LoomApp {
|
|
20
|
+
providers = new Map();
|
|
21
|
+
services = [];
|
|
22
|
+
factories = [];
|
|
23
|
+
components = [];
|
|
24
|
+
_started = false;
|
|
25
|
+
// ── Event bus (delegates to the module-level bus singleton) ──
|
|
26
|
+
/** Subscribe to a typed event. Returns unsubscribe function. */
|
|
27
|
+
on(type, handler) { return bus.on(type, handler); }
|
|
28
|
+
/** Emit a typed event to all listeners. */
|
|
29
|
+
emit(event) { bus.emit(event); }
|
|
30
|
+
/** Remove a specific event handler. */
|
|
31
|
+
off(type, handler) { bus.off(type, handler); }
|
|
32
|
+
use(keyOrThing, instance) {
|
|
33
|
+
if (instance !== undefined) {
|
|
34
|
+
// Explicit key: app.use(Key, value)
|
|
35
|
+
this.providers.set(keyOrThing, instance);
|
|
36
|
+
}
|
|
37
|
+
else if (typeof keyOrThing === "function") {
|
|
38
|
+
if (keyOrThing.prototype?.constructor === keyOrThing) {
|
|
39
|
+
// Class constructor → instantiate
|
|
40
|
+
this.providers.set(keyOrThing, new keyOrThing());
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Factory function → call it
|
|
44
|
+
const result = keyOrThing();
|
|
45
|
+
this.providers.set(result.constructor, result);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Instance → key from constructor
|
|
50
|
+
this.providers.set(keyOrThing.constructor, keyOrThing);
|
|
51
|
+
}
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
// ── Registration (called by decorators) ──
|
|
55
|
+
/** Queue a @service class for auto-instantiation on start() */
|
|
56
|
+
registerService(ctor) {
|
|
57
|
+
this.services.push(ctor);
|
|
58
|
+
}
|
|
59
|
+
/** Queue a @factory method for invocation on start() */
|
|
60
|
+
registerFactory(key, proto, method) {
|
|
61
|
+
this.factories.push({ proto, method, key });
|
|
62
|
+
}
|
|
63
|
+
/** Queue a @component for customElements.define() on start() */
|
|
64
|
+
register(tag, ctor) {
|
|
65
|
+
this.components.push({ tag, ctor });
|
|
66
|
+
}
|
|
67
|
+
// ── Resolution ──
|
|
68
|
+
/** Retrieve a provider/service by key. Throws if missing. */
|
|
69
|
+
get(key) {
|
|
70
|
+
const v = this.providers.get(key);
|
|
71
|
+
if (v === undefined) {
|
|
72
|
+
throw new Error(`[loom] no provider for ${key?.name ?? key}`);
|
|
73
|
+
}
|
|
74
|
+
return v;
|
|
75
|
+
}
|
|
76
|
+
/** Retrieve a provider/service, or undefined if not registered. */
|
|
77
|
+
maybe(key) {
|
|
78
|
+
return this.providers.get(key);
|
|
79
|
+
}
|
|
80
|
+
/** Resolve @inject parameter metadata for a constructor or method. */
|
|
81
|
+
resolveParams(proto, method) {
|
|
82
|
+
const meta = proto?.[INJECT_PARAMS] ?? [];
|
|
83
|
+
const params = meta
|
|
84
|
+
.filter((m) => m.method === method)
|
|
85
|
+
.sort((a, b) => a.index - b.index);
|
|
86
|
+
return params.map((m) => this.get(m.key));
|
|
87
|
+
}
|
|
88
|
+
// ── Lifecycle ──
|
|
89
|
+
/**
|
|
90
|
+
* Boot the app:
|
|
91
|
+
* 1. Auto-instantiate @service singletons (with constructor @inject)
|
|
92
|
+
* 2. Run @factory methods (with parameter @inject)
|
|
93
|
+
* 3. Start the render loop
|
|
94
|
+
* 4. Register all @component custom elements
|
|
95
|
+
*/
|
|
96
|
+
async start() {
|
|
97
|
+
if (this._started)
|
|
98
|
+
return;
|
|
99
|
+
// 1. Instantiate @service singletons and wire @on handlers
|
|
100
|
+
for (const Svc of this.services) {
|
|
101
|
+
if (!this.providers.has(Svc)) {
|
|
102
|
+
const args = this.resolveParams(Svc.prototype, "constructor");
|
|
103
|
+
this.providers.set(Svc, new Svc(...args));
|
|
104
|
+
}
|
|
105
|
+
// Wire @on event handlers (bus events + DOM events)
|
|
106
|
+
const instance = this.providers.get(Svc);
|
|
107
|
+
for (const handler of instance[ON_HANDLERS] ?? []) {
|
|
108
|
+
if (handler.domTarget) {
|
|
109
|
+
// DOM EventTarget: @on(window, "resize")
|
|
110
|
+
handler.domTarget.addEventListener(handler.event, (e) => instance[handler.key](e));
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Bus event: @on(ColorSelect)
|
|
114
|
+
bus.on(handler.type, (e) => instance[handler.key](e));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// 2. Run @factory methods on instantiated services
|
|
119
|
+
for (const { proto, method, key } of this.factories) {
|
|
120
|
+
// Find the service instance that owns this factory method
|
|
121
|
+
const svc = this.providers.get(proto.constructor);
|
|
122
|
+
if (!svc)
|
|
123
|
+
continue;
|
|
124
|
+
const args = this.resolveParams(proto, method);
|
|
125
|
+
const result = await svc[method](...args);
|
|
126
|
+
if (result != null) {
|
|
127
|
+
this.providers.set(key ?? result.constructor, result);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// 3. Start render loop
|
|
131
|
+
renderLoop.start();
|
|
132
|
+
// 4. Register all queued custom elements
|
|
133
|
+
for (const { tag, ctor } of this.components) {
|
|
134
|
+
if (!customElements.get(tag)) {
|
|
135
|
+
customElements.define(tag, ctor);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
this._started = true;
|
|
139
|
+
}
|
|
140
|
+
/** Tear down — stop render loop */
|
|
141
|
+
stop() {
|
|
142
|
+
renderLoop.stop();
|
|
143
|
+
this._started = false;
|
|
144
|
+
}
|
|
145
|
+
/** Whether the app has been started */
|
|
146
|
+
get started() {
|
|
147
|
+
return this._started;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** Module-level singleton — the Loom app instance */
|
|
151
|
+
export const app = new LoomApp();
|
|
152
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}
|
package/dist/bus.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom — EventBus (type-discriminated)
|
|
3
|
+
*
|
|
4
|
+
* Events are classes. The class constructor IS the channel key.
|
|
5
|
+
* No string registry, full type inference.
|
|
6
|
+
*/
|
|
7
|
+
import type { LoomEvent } from "./event";
|
|
8
|
+
export type Constructor<T = any> = new (...args: any[]) => T;
|
|
9
|
+
export type Handler<T> = (data: T) => void;
|
|
10
|
+
export declare class EventBus {
|
|
11
|
+
private listeners;
|
|
12
|
+
/** Subscribe to a typed event. Returns unsubscribe function. */
|
|
13
|
+
on<T>(type: Constructor<T>, handler: Handler<T>): () => void;
|
|
14
|
+
/** Emit a typed event — dispatches to all handlers of that class */
|
|
15
|
+
emit<T extends LoomEvent>(event: T): void;
|
|
16
|
+
/** Remove a specific handler */
|
|
17
|
+
off<T>(type: Constructor<T>, handler: Handler<T>): void;
|
|
18
|
+
/** Remove all listeners for a given event type */
|
|
19
|
+
clear<T>(type: Constructor<T>): void;
|
|
20
|
+
/** Remove ALL listeners */
|
|
21
|
+
reset(): void;
|
|
22
|
+
}
|
|
23
|
+
/** Module-level default bus — used by LoomElement and available via import */
|
|
24
|
+
export declare let bus: EventBus;
|
|
25
|
+
/** Swap the global bus (for test isolation) */
|
|
26
|
+
export declare function useBus(b: EventBus): void;
|
|
27
|
+
//# sourceMappingURL=bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../src/bus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,GAAG,IAAI,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAC7D,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;AAE3C,qBAAa,QAAQ;IACnB,OAAO,CAAC,SAAS,CAA6C;IAE9D,gEAAgE;IAChE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAO5D,oEAAoE;IACpE,IAAI,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAKzC,gCAAgC;IAChC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI;IAIvD,kDAAkD;IAClD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI;IAIpC,2BAA2B;IAC3B,KAAK,IAAI,IAAI;CAGd;AAED,8EAA8E;AAC9E,eAAO,IAAI,GAAG,UAAiB,CAAC;AAEhC,+CAA+C;AAC/C,wBAAgB,MAAM,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI,CAExC"}
|
package/dist/bus.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom — EventBus (type-discriminated)
|
|
3
|
+
*
|
|
4
|
+
* Events are classes. The class constructor IS the channel key.
|
|
5
|
+
* No string registry, full type inference.
|
|
6
|
+
*/
|
|
7
|
+
export class EventBus {
|
|
8
|
+
listeners = new Map();
|
|
9
|
+
/** Subscribe to a typed event. Returns unsubscribe function. */
|
|
10
|
+
on(type, handler) {
|
|
11
|
+
if (!this.listeners.has(type))
|
|
12
|
+
this.listeners.set(type, new Set());
|
|
13
|
+
const set = this.listeners.get(type);
|
|
14
|
+
set.add(handler);
|
|
15
|
+
return () => set.delete(handler);
|
|
16
|
+
}
|
|
17
|
+
/** Emit a typed event — dispatches to all handlers of that class */
|
|
18
|
+
emit(event) {
|
|
19
|
+
const ctor = event.constructor;
|
|
20
|
+
this.listeners.get(ctor)?.forEach((h) => h(event));
|
|
21
|
+
}
|
|
22
|
+
/** Remove a specific handler */
|
|
23
|
+
off(type, handler) {
|
|
24
|
+
this.listeners.get(type)?.delete(handler);
|
|
25
|
+
}
|
|
26
|
+
/** Remove all listeners for a given event type */
|
|
27
|
+
clear(type) {
|
|
28
|
+
this.listeners.delete(type);
|
|
29
|
+
}
|
|
30
|
+
/** Remove ALL listeners */
|
|
31
|
+
reset() {
|
|
32
|
+
this.listeners.clear();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Module-level default bus — used by LoomElement and available via import */
|
|
36
|
+
export let bus = new EventBus();
|
|
37
|
+
/** Swap the global bus (for test isolation) */
|
|
38
|
+
export function useBus(b) {
|
|
39
|
+
bus = b;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=bus.js.map
|
package/dist/bus.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.js","sourceRoot":"","sources":["../src/bus.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,OAAO,QAAQ;IACX,SAAS,GAAG,IAAI,GAAG,EAAkC,CAAC;IAE9D,gEAAgE;IAChE,EAAE,CAAI,IAAoB,EAAE,OAAmB;QAC7C,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACnE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;QACtC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjB,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,oEAAoE;IACpE,IAAI,CAAsB,KAAQ;QAChC,MAAM,IAAI,GAAI,KAAgB,CAAC,WAA0B,CAAC;QAC1D,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,gCAAgC;IAChC,GAAG,CAAI,IAAoB,EAAE,OAAmB;QAC9C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,kDAAkD;IAClD,KAAK,CAAI,IAAoB;QAC3B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,2BAA2B;IAC3B,KAAK;QACH,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACF;AAED,8EAA8E;AAC9E,MAAM,CAAC,IAAI,GAAG,GAAG,IAAI,QAAQ,EAAE,CAAC;AAEhC,+CAA+C;AAC/C,MAAM,UAAU,MAAM,CAAC,CAAW;IAChC,GAAG,GAAG,CAAC,CAAC;AACV,CAAC"}
|
package/dist/css.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom — CSS tagged template + cache
|
|
3
|
+
*
|
|
4
|
+
* Standalone css`` tag for creating reusable, cached CSSStyleSheets.
|
|
5
|
+
* Same CSS string → same CSSStyleSheet instance (deduped across components).
|
|
6
|
+
*/
|
|
7
|
+
/** Allowed types for css`` interpolation */
|
|
8
|
+
export type CSSValue = string | number;
|
|
9
|
+
/**
|
|
10
|
+
* Tagged template for creating cached CSSStyleSheets.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const styles = css`
|
|
14
|
+
* :host { display: block; }
|
|
15
|
+
* button { padding: ${8}px; background: ${"#ff6b6b"}; }
|
|
16
|
+
* `;
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function css(strings: TemplateStringsArray, ...values: CSSValue[]): CSSStyleSheet;
|
|
20
|
+
/**
|
|
21
|
+
* Adopt a stylesheet into a shadow root from either a tagged template or string.
|
|
22
|
+
* Used internally by LoomElement.css().
|
|
23
|
+
*/
|
|
24
|
+
export declare function adoptCSS(shadow: ShadowRoot, stringsOrText: string | TemplateStringsArray, ...values: CSSValue[]): void;
|
|
25
|
+
//# sourceMappingURL=css.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,4CAA4C;AAC5C,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAKvC;;;;;;;;;GASG;AACH,wBAAgB,GAAG,CACjB,OAAO,EAAE,oBAAoB,EAC7B,GAAG,MAAM,EAAE,QAAQ,EAAE,GACpB,aAAa,CASf;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CACtB,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,MAAM,GAAG,oBAAoB,EAC5C,GAAG,MAAM,EAAE,QAAQ,EAAE,GACpB,IAAI,CAgBN"}
|
package/dist/css.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loom — CSS tagged template + cache
|
|
3
|
+
*
|
|
4
|
+
* Standalone css`` tag for creating reusable, cached CSSStyleSheets.
|
|
5
|
+
* Same CSS string → same CSSStyleSheet instance (deduped across components).
|
|
6
|
+
*/
|
|
7
|
+
/** Global cache — same CSS string → same CSSStyleSheet instance */
|
|
8
|
+
const cssCache = new Map();
|
|
9
|
+
/**
|
|
10
|
+
* Tagged template for creating cached CSSStyleSheets.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* const styles = css`
|
|
14
|
+
* :host { display: block; }
|
|
15
|
+
* button { padding: ${8}px; background: ${"#ff6b6b"}; }
|
|
16
|
+
* `;
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function css(strings, ...values) {
|
|
20
|
+
const text = String.raw(strings, ...values);
|
|
21
|
+
let sheet = cssCache.get(text);
|
|
22
|
+
if (!sheet) {
|
|
23
|
+
sheet = new CSSStyleSheet();
|
|
24
|
+
sheet.replaceSync(text);
|
|
25
|
+
cssCache.set(text, sheet);
|
|
26
|
+
}
|
|
27
|
+
return sheet;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Adopt a stylesheet into a shadow root from either a tagged template or string.
|
|
31
|
+
* Used internally by LoomElement.css().
|
|
32
|
+
*/
|
|
33
|
+
export function adoptCSS(shadow, stringsOrText, ...values) {
|
|
34
|
+
const text = typeof stringsOrText === "string"
|
|
35
|
+
? stringsOrText
|
|
36
|
+
: String.raw(stringsOrText, ...values);
|
|
37
|
+
const sheet = cssCache.get(text) ??
|
|
38
|
+
(() => {
|
|
39
|
+
const s = new CSSStyleSheet();
|
|
40
|
+
s.replaceSync(text);
|
|
41
|
+
cssCache.set(text, s);
|
|
42
|
+
return s;
|
|
43
|
+
})();
|
|
44
|
+
if (!shadow.adoptedStyleSheets.includes(sheet)) {
|
|
45
|
+
shadow.adoptedStyleSheets = [...shadow.adoptedStyleSheets, sheet];
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=css.js.map
|
package/dist/css.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"css.js","sourceRoot":"","sources":["../src/css.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,mEAAmE;AACnE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,UAAU,GAAG,CACjB,OAA6B,EAC7B,GAAG,MAAkB;IAErB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC;IAC5C,IAAI,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,KAAK,GAAG,IAAI,aAAa,EAAE,CAAC;QAC5B,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACxB,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAC5B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CACtB,MAAkB,EAClB,aAA4C,EAC5C,GAAG,MAAkB;IAErB,MAAM,IAAI,GACR,OAAO,aAAa,KAAK,QAAQ;QAC/B,CAAC,CAAC,aAAa;QACf,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,MAAM,CAAC,CAAC;IAC3C,MAAM,KAAK,GACT,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;QAClB,CAAC,GAAG,EAAE;YACJ,MAAM,CAAC,GAAG,IAAI,aAAa,EAAE,CAAC;YAC9B,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACpB,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACtB,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,EAAE,CAAC;IACP,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,MAAM,CAAC,kBAAkB,GAAG,CAAC,GAAG,MAAM,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;IACpE,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Register a class as a custom element. Wires @prop observed attributes
|
|
3
|
+
* and attributeChangedCallback auto-parsing.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* @component("my-counter")
|
|
7
|
+
* class MyCounter extends LoomElement { ... }
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export declare function component(tag: string): (ctor: any) => void;
|
|
11
|
+
//# sourceMappingURL=component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/decorators/component.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,IAC3B,MAAM,GAAG,UA8BlB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { PROPS } from "./symbols";
|
|
2
|
+
import { app } from "../app";
|
|
3
|
+
/**
|
|
4
|
+
* Register a class as a custom element. Wires @prop observed attributes
|
|
5
|
+
* and attributeChangedCallback auto-parsing.
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* @component("my-counter")
|
|
9
|
+
* class MyCounter extends LoomElement { ... }
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export function component(tag) {
|
|
13
|
+
return (ctor) => {
|
|
14
|
+
const propMap = ctor[PROPS] ?? new Map();
|
|
15
|
+
// Wire observedAttributes from @prop fields
|
|
16
|
+
Object.defineProperty(ctor, "observedAttributes", {
|
|
17
|
+
get: () => [...propMap.keys()],
|
|
18
|
+
});
|
|
19
|
+
// Wire attributeChangedCallback to update @prop fields
|
|
20
|
+
const origCallback = ctor.prototype.attributeChangedCallback;
|
|
21
|
+
ctor.prototype.attributeChangedCallback = function (name, _old, val) {
|
|
22
|
+
const field = propMap.get(name);
|
|
23
|
+
if (field && val !== null) {
|
|
24
|
+
const current = this[field];
|
|
25
|
+
if (typeof current === "number")
|
|
26
|
+
this[field] = Number(val);
|
|
27
|
+
else if (typeof current === "boolean")
|
|
28
|
+
this[field] = val !== null && val !== "false";
|
|
29
|
+
else
|
|
30
|
+
this[field] = val;
|
|
31
|
+
}
|
|
32
|
+
origCallback?.call(this, name, _old, val);
|
|
33
|
+
};
|
|
34
|
+
app.register(tag, ctor);
|
|
35
|
+
ctor.__loom_tag = tag;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component.js","sourceRoot":"","sources":["../../src/decorators/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAC;AAE7B;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,OAAO,CAAC,IAAS,EAAE,EAAE;QACnB,MAAM,OAAO,GACV,IAAY,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;QAEpC,4CAA4C;QAC5C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,oBAAoB,EAAE;YAChD,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;SAC/B,CAAC,CAAC;QAEH,uDAAuD;QACvD,MAAM,YAAY,GAAI,IAAI,CAAC,SAAiB,CAAC,wBAAwB,CAAC;QACrE,IAAI,CAAC,SAAiB,CAAC,wBAAwB,GAAG,UACjD,IAAY,EACZ,IAAmB,EACnB,GAAkB;YAElB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;gBAC1B,MAAM,OAAO,GAAI,IAAY,CAAC,KAAK,CAAC,CAAC;gBACrC,IAAI,OAAO,OAAO,KAAK,QAAQ;oBAAG,IAAY,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;qBAC/D,IAAI,OAAO,OAAO,KAAK,SAAS;oBAClC,IAAY,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,OAAO,CAAC;;oBACnD,IAAY,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC;YAClC,CAAC;YACD,YAAY,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QAC5C,CAAC,CAAC;QAEF,GAAG,CAAC,QAAQ,CAAC,GAAG,EAAE,IAAgC,CAAC,CAAC;QACnD,IAAY,CAAC,UAAU,GAAG,GAAG,CAAC;IACjC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-instantiated singleton. Registered on app.start().
|
|
3
|
+
* Constructor @inject params are resolved automatically.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* @service
|
|
7
|
+
* class BookmarkStore extends CollectionStore<Bookmark> { ... }
|
|
8
|
+
* ```
|
|
9
|
+
*/
|
|
10
|
+
export declare function service(ctor: any): void;
|
|
11
|
+
/**
|
|
12
|
+
* Dual-mode dependency injection.
|
|
13
|
+
*
|
|
14
|
+
* Property: @inject(Foo) foo!: Foo;
|
|
15
|
+
* Constructor: constructor(@inject(Config) config: Config)
|
|
16
|
+
* Factory arg: createChat(@inject(NatsConn) nc: NatsConn)
|
|
17
|
+
*
|
|
18
|
+
* T is optional — use for explicit typing if desired.
|
|
19
|
+
*/
|
|
20
|
+
export declare function inject<T = any>(key: any): (target: any, propOrMethod: string | undefined, index?: number) => void;
|
|
21
|
+
/**
|
|
22
|
+
* Method decorator on @service classes.
|
|
23
|
+
* Return value is registered as a provider on app.start().
|
|
24
|
+
* Supports @inject on parameters. Async methods are awaited.
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* @service
|
|
28
|
+
* class Boot {
|
|
29
|
+
* @factory(ChatServiceNatsClient)
|
|
30
|
+
* createChat(@inject(NatsConnection) nc: NatsConnection) {
|
|
31
|
+
* return new ChatServiceNatsClient(nc);
|
|
32
|
+
* }
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function factory(key?: any): (target: any, method: string) => void;
|
|
37
|
+
//# sourceMappingURL=di.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"di.d.ts","sourceRoot":"","sources":["../../src/decorators/di.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAEvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,IAC9B,QAAQ,GAAG,EAAE,cAAc,MAAM,GAAG,SAAS,EAAE,QAAQ,MAAM,UAiBtE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,OAAO,CAAC,GAAG,CAAC,EAAE,GAAG,IACvB,QAAQ,GAAG,EAAE,QAAQ,MAAM,UAGpC"}
|