react-runtime-pipe 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 +624 -0
- package/build/action/compose.d.ts +7 -0
- package/build/action/defineAction.d.ts +2 -0
- package/build/action/executeAction.d.ts +6 -0
- package/build/action/index.d.ts +4 -0
- package/build/action/types.d.ts +48 -0
- package/build/action/utils.d.ts +1 -0
- package/build/app/container.d.ts +17 -0
- package/build/app/createApp.d.ts +21 -0
- package/build/app/index.d.ts +3 -0
- package/build/app/plugin.d.ts +10 -0
- package/build/cjs/index.js +1 -0
- package/build/cjs/middleware/index.js +1 -0
- package/build/cjs/plugin/index.js +1 -0
- package/build/cjs/react/index.js +1 -0
- package/build/esm/index.js +1 -0
- package/build/esm/middleware/index.js +1 -0
- package/build/esm/plugin/index.js +1 -0
- package/build/esm/react/index.js +1 -0
- package/build/index.d.ts +2 -0
- package/build/middleware/abort.d.ts +2 -0
- package/build/middleware/auth.d.ts +2 -0
- package/build/middleware/cache.d.ts +2 -0
- package/build/middleware/event.d.ts +2 -0
- package/build/middleware/index.d.ts +7 -0
- package/build/middleware/logger.d.ts +2 -0
- package/build/middleware/retry.d.ts +2 -0
- package/build/middleware/timing.d.ts +2 -0
- package/build/plugin/authPlugin.d.ts +2 -0
- package/build/plugin/cachePlugin.d.ts +2 -0
- package/build/plugin/devtoolsPlugin.d.ts +4 -0
- package/build/plugin/eventPlugin.d.ts +3 -0
- package/build/plugin/index.d.ts +6 -0
- package/build/plugin/loggerPlugin.d.ts +2 -0
- package/build/plugin/loggerPlusPlugin.d.ts +2 -0
- package/build/react/index.d.ts +4 -0
- package/build/react/provider.d.ts +7 -0
- package/build/react/useAction.d.ts +10 -0
- package/build/react/useEventBus.d.ts +2 -0
- package/build/react/useService.d.ts +2 -0
- package/package.json +130 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Delpi.Kye
|
|
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,624 @@
|
|
|
1
|
+
# ⚡ react-runtime-pipe
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/react-runtime-pipe)  
|
|
4
|
+
|
|
5
|
+
[LIVE EXAMPLE](https://codesandbox.io/p/sandbox/ps2sp8)
|
|
6
|
+
|
|
7
|
+
🚀 A backend-inspired execution runtime for React applications.
|
|
8
|
+
|
|
9
|
+
> Build scalable frontend systems using middleware pipelines, typed actions, dependency injection, runtime context, plugins, orchestration, and event-driven execution.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install react-runtime-pipe
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
# What is react-runtime-pipe?
|
|
20
|
+
|
|
21
|
+
`react-runtime-pipe` is a lightweight runtime for executing backend-style logic in React applications.
|
|
22
|
+
|
|
23
|
+
It brings concepts like middleware, dependency injection, and orchestration into frontend code.
|
|
24
|
+
|
|
25
|
+
It provides:
|
|
26
|
+
|
|
27
|
+
- typed action execution
|
|
28
|
+
- middleware pipelines
|
|
29
|
+
- dependency injection
|
|
30
|
+
- runtime context
|
|
31
|
+
- plugins and event system
|
|
32
|
+
- retry and cancellation support
|
|
33
|
+
|
|
34
|
+
without enforcing any specific state management approach.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
# Why react-runtime-pipe?
|
|
39
|
+
|
|
40
|
+
Frontend apps often become hard to scale as logic spreads across components and async flows become inconsistent.
|
|
41
|
+
|
|
42
|
+
Common problems:
|
|
43
|
+
|
|
44
|
+
- business logic inside UI components
|
|
45
|
+
- duplicated async logic
|
|
46
|
+
- scattered dependencies
|
|
47
|
+
- tightly coupled side effects
|
|
48
|
+
- inconsistent orchestration
|
|
49
|
+
|
|
50
|
+
react-runtime-pipe solves this by introducing a structured execution layer:
|
|
51
|
+
|
|
52
|
+
- predictable execution flow
|
|
53
|
+
- composable middleware
|
|
54
|
+
- centralized orchestration
|
|
55
|
+
- backend-like architecture in frontend
|
|
56
|
+
- framework-agnostic design
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
# Features
|
|
61
|
+
|
|
62
|
+
- ⚡ Runtime-first architecture
|
|
63
|
+
- 🧩 Middleware pipeline
|
|
64
|
+
- 🔌 Plugin system
|
|
65
|
+
- 🧠 Typed execution context
|
|
66
|
+
- 🛡 Action lifecycle hooks
|
|
67
|
+
- ♻️ Retry orchestration
|
|
68
|
+
- 📦 Dependency injection container
|
|
69
|
+
- 📡 Event bus system
|
|
70
|
+
- 📝 Full TypeScript inference
|
|
71
|
+
- 🚦 Abort/cancellation support
|
|
72
|
+
- 💾 Scoped execution cache
|
|
73
|
+
- ⚛️ React integration
|
|
74
|
+
- 🧱 Framework agnostic core
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
# Mental Model
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
UI Event
|
|
82
|
+
↓
|
|
83
|
+
useAction()
|
|
84
|
+
↓
|
|
85
|
+
createExecutionContext()
|
|
86
|
+
↓
|
|
87
|
+
Plugins
|
|
88
|
+
↓
|
|
89
|
+
Middleware Pipeline
|
|
90
|
+
↓
|
|
91
|
+
Action Handler
|
|
92
|
+
↓
|
|
93
|
+
Service Layer
|
|
94
|
+
↓
|
|
95
|
+
Event Bus
|
|
96
|
+
↓
|
|
97
|
+
UI Update
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
# Quick Start
|
|
103
|
+
|
|
104
|
+
## Create App
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
// app.ts
|
|
108
|
+
import { createApp } from 'react-runtime-pipe'
|
|
109
|
+
import { loggerPlugin } from 'react-runtime-pipe/plugin'
|
|
110
|
+
|
|
111
|
+
import { ApiService } from './services/api'
|
|
112
|
+
import { ApiServiceToken } from './tokens'
|
|
113
|
+
|
|
114
|
+
export const app = createApp({
|
|
115
|
+
services: [
|
|
116
|
+
{
|
|
117
|
+
token: ApiServiceToken,
|
|
118
|
+
useValue: new ApiService(),
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
app.plugin(loggerPlugin)
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Define Action
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { defineAction } from 'react-runtime-pipe'
|
|
133
|
+
import { globalEvents } from 'react-runtime-pipe/react'
|
|
134
|
+
import { logger, retry } from 'react-runtime-pipe/middleware'
|
|
135
|
+
|
|
136
|
+
import { ApiServiceToken } from './tokens'
|
|
137
|
+
|
|
138
|
+
export const saveUserAction = defineAction({
|
|
139
|
+
name: 'save-user',
|
|
140
|
+
|
|
141
|
+
middleware: [logger, retry(3)],
|
|
142
|
+
|
|
143
|
+
async handler({ input, context }) {
|
|
144
|
+
context.logger.info('saving user', input)
|
|
145
|
+
|
|
146
|
+
const api = context.container.resolve(ApiServiceToken)
|
|
147
|
+
|
|
148
|
+
const user = await api.saveUser(input)
|
|
149
|
+
|
|
150
|
+
// context.events.$emit('user:saved', user)
|
|
151
|
+
globalEvents.$emit('user:saved', user)
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
success: true,
|
|
155
|
+
user,
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Service Implementation
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
// api.ts
|
|
167
|
+
import { createRequestId } from 'react-runtime-pipe'
|
|
168
|
+
|
|
169
|
+
export class ApiService {
|
|
170
|
+
async saveUser(input: any) {
|
|
171
|
+
await new Promise((resolve) => {
|
|
172
|
+
setTimeout(resolve, 500)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
id: createRequestId(),
|
|
177
|
+
input,
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Tokens
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
// tokens.ts
|
|
189
|
+
import { createToken } from 'react-runtime-pipe'
|
|
190
|
+
|
|
191
|
+
import type { ApiService } from './services/api'
|
|
192
|
+
|
|
193
|
+
export const ApiServiceToken =
|
|
194
|
+
createToken<ApiService>('ApiService')
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## React Usage
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
// Root.tsx
|
|
203
|
+
import React from 'react'
|
|
204
|
+
|
|
205
|
+
import {
|
|
206
|
+
useAction,
|
|
207
|
+
useEventBus,
|
|
208
|
+
globalEvents,
|
|
209
|
+
} from 'react-runtime-pipe/react'
|
|
210
|
+
|
|
211
|
+
import { saveUserAction } from './saveUser'
|
|
212
|
+
|
|
213
|
+
export function Root() {
|
|
214
|
+
const saveUser = useAction(saveUserAction)
|
|
215
|
+
|
|
216
|
+
useEventBus(
|
|
217
|
+
'user:saved',
|
|
218
|
+
() => {
|
|
219
|
+
alert('User saved successfully!')
|
|
220
|
+
},
|
|
221
|
+
globalEvents,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
<div>
|
|
226
|
+
<button
|
|
227
|
+
disabled={saveUser.loading}
|
|
228
|
+
onClick={() => {
|
|
229
|
+
saveUser.execute({
|
|
230
|
+
name: 'Tung',
|
|
231
|
+
})
|
|
232
|
+
}}
|
|
233
|
+
>
|
|
234
|
+
{saveUser.loading
|
|
235
|
+
? 'Saving...'
|
|
236
|
+
: 'Save User'}
|
|
237
|
+
</button>
|
|
238
|
+
|
|
239
|
+
{saveUser.error ? (
|
|
240
|
+
<pre>{String(saveUser.error)}</pre>
|
|
241
|
+
) : null}
|
|
242
|
+
</div>
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Bootstrap
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
// main.tsx
|
|
253
|
+
import React from 'react'
|
|
254
|
+
import ReactDOM from 'react-dom/client'
|
|
255
|
+
|
|
256
|
+
import { RuntimeProvider } from 'react-runtime-pipe/react'
|
|
257
|
+
|
|
258
|
+
import { Root } from './Root'
|
|
259
|
+
import { app } from './app'
|
|
260
|
+
|
|
261
|
+
async function bootstrap() {
|
|
262
|
+
await app.run()
|
|
263
|
+
|
|
264
|
+
const rootElement =
|
|
265
|
+
document.getElementById('root')
|
|
266
|
+
|
|
267
|
+
if (!rootElement) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
'Root element #root not found',
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
ReactDOM.createRoot(rootElement).render(
|
|
274
|
+
<React.StrictMode>
|
|
275
|
+
<RuntimeProvider app={app}>
|
|
276
|
+
<App />
|
|
277
|
+
</RuntimeProvider>
|
|
278
|
+
</React.StrictMode>,
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
bootstrap()
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
# Dependency Injection
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
import { createApp, createToken } from 'react-runtime-pipe'
|
|
291
|
+
|
|
292
|
+
class ApiService {
|
|
293
|
+
async saveUser() {}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const ApiServiceToken = createToken<ApiService>('ApiService')
|
|
297
|
+
|
|
298
|
+
const app = createApp({
|
|
299
|
+
services: [
|
|
300
|
+
{
|
|
301
|
+
token: ApiServiceToken,
|
|
302
|
+
useFactory: () =>
|
|
303
|
+
new ApiService(),
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
})
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Service Injection in Actions
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
import { defineAction } from 'react-runtime-pipe'
|
|
315
|
+
|
|
316
|
+
import { ApiServiceToken } from './tokens'
|
|
317
|
+
|
|
318
|
+
export const saveUserAction =
|
|
319
|
+
defineAction({
|
|
320
|
+
name: 'save-user',
|
|
321
|
+
|
|
322
|
+
async handler({
|
|
323
|
+
input,
|
|
324
|
+
context,
|
|
325
|
+
}) {
|
|
326
|
+
const api =
|
|
327
|
+
context.container.resolve(
|
|
328
|
+
ApiServiceToken,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
return api.saveUser(input)
|
|
332
|
+
},
|
|
333
|
+
})
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## useService hook
|
|
339
|
+
|
|
340
|
+
```tsx
|
|
341
|
+
import { useService } from 'react-runtime-pipe/react'
|
|
342
|
+
|
|
343
|
+
import { ApiServiceToken } from './tokens'
|
|
344
|
+
|
|
345
|
+
export function UserPage() {
|
|
346
|
+
const api = useService(ApiServiceToken)
|
|
347
|
+
|
|
348
|
+
return null
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
# Runtime Context
|
|
355
|
+
|
|
356
|
+
| Property | Description |
|
|
357
|
+
|---------------|--------------------------|
|
|
358
|
+
| ctx.requestId | execution request id |
|
|
359
|
+
| ctx.signal | abort signal |
|
|
360
|
+
| ctx.logger | runtime logger |
|
|
361
|
+
| ctx.container | dependency container |
|
|
362
|
+
| ctx.events | execution event bus |
|
|
363
|
+
| ctx.cache | scoped execution cache |
|
|
364
|
+
| ctx.auth | authenticated user state |
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
# Event Bus
|
|
369
|
+
|
|
370
|
+
## Emit Event
|
|
371
|
+
|
|
372
|
+
```ts
|
|
373
|
+
globalEvents.$emit(
|
|
374
|
+
'user:saved',
|
|
375
|
+
user,
|
|
376
|
+
)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## Listen To Event
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
useEventBus(
|
|
385
|
+
'user:saved',
|
|
386
|
+
(payload) => {
|
|
387
|
+
console.log(payload)
|
|
388
|
+
},
|
|
389
|
+
globalEvents,
|
|
390
|
+
)
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
# Middleware
|
|
396
|
+
|
|
397
|
+
Middleware in `react-runtime-pipe` allows you to intercept and control the execution flow of an action.
|
|
398
|
+
|
|
399
|
+
Here is a simple example: **Abort Middleware** — used to stop execution if the request has been cancelled.
|
|
400
|
+
|
|
401
|
+
```ts
|
|
402
|
+
import type { Middleware } from 'react-runtime-pipe'
|
|
403
|
+
|
|
404
|
+
export const abortMiddleware: Middleware = async ({
|
|
405
|
+
context,
|
|
406
|
+
next,
|
|
407
|
+
}) => {
|
|
408
|
+
// check before executing the handler
|
|
409
|
+
context.throwIfAborted()
|
|
410
|
+
|
|
411
|
+
const result = await next()
|
|
412
|
+
|
|
413
|
+
// check after the handler finishes
|
|
414
|
+
context.throwIfAborted()
|
|
415
|
+
|
|
416
|
+
return result
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
This middleware ensures that if the user triggers an abort (e.g. cancels a request or unmounts a component), the action is immediately stopped both before and after execution.
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
# Plugins
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
import type { Plugin } from 'react-runtime-pipe'
|
|
428
|
+
|
|
429
|
+
const analyticsPlugin: Plugin = {
|
|
430
|
+
name: 'analytics',
|
|
431
|
+
|
|
432
|
+
setup({ app }) {
|
|
433
|
+
console.log('plugin setup')
|
|
434
|
+
},
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
app.plugin(analyticsPlugin)
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
# React Runtime Provider
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
import { RuntimeProvider } from 'react-runtime-pipe/react'
|
|
446
|
+
|
|
447
|
+
import { app } from './app'
|
|
448
|
+
|
|
449
|
+
export function Root() {
|
|
450
|
+
return (
|
|
451
|
+
<RuntimeProvider app={app}>
|
|
452
|
+
<App />
|
|
453
|
+
</RuntimeProvider>
|
|
454
|
+
)
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
---
|
|
459
|
+
|
|
460
|
+
# Action Lifecycle
|
|
461
|
+
|
|
462
|
+
```text
|
|
463
|
+
useAction.execute()
|
|
464
|
+
↓
|
|
465
|
+
createExecutionContext()
|
|
466
|
+
↓
|
|
467
|
+
composeMiddleware()
|
|
468
|
+
↓
|
|
469
|
+
executeHandler()
|
|
470
|
+
↓
|
|
471
|
+
emitEvents()
|
|
472
|
+
↓
|
|
473
|
+
returnResult()
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
# Execution Cache
|
|
479
|
+
|
|
480
|
+
```ts
|
|
481
|
+
const cacheKey = 'user:1'
|
|
482
|
+
|
|
483
|
+
if (ctx.cache.has(cacheKey)) {
|
|
484
|
+
return ctx.cache.get(cacheKey)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const user = await api.getUser()
|
|
488
|
+
|
|
489
|
+
ctx.cache.set(cacheKey, user)
|
|
490
|
+
|
|
491
|
+
return user
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
# Abort Support
|
|
497
|
+
|
|
498
|
+
```ts
|
|
499
|
+
ctx.throwIfAborted()
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
```ts
|
|
503
|
+
ctx.signal.addEventListener(
|
|
504
|
+
'abort',
|
|
505
|
+
() => {
|
|
506
|
+
console.log('cancelled')
|
|
507
|
+
},
|
|
508
|
+
)
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
# Full Example
|
|
514
|
+
|
|
515
|
+
```ts
|
|
516
|
+
import { defineAction } from 'react-runtime-pipe'
|
|
517
|
+
|
|
518
|
+
import {
|
|
519
|
+
logger,
|
|
520
|
+
retry,
|
|
521
|
+
auth,
|
|
522
|
+
} from 'react-runtime-pipe/middleware'
|
|
523
|
+
|
|
524
|
+
import { ApiServiceToken } from './tokens'
|
|
525
|
+
|
|
526
|
+
import { globalEvents } from './events'
|
|
527
|
+
|
|
528
|
+
export const saveUserAction =
|
|
529
|
+
defineAction({
|
|
530
|
+
name: 'save-user',
|
|
531
|
+
|
|
532
|
+
middleware: [
|
|
533
|
+
auth,
|
|
534
|
+
logger,
|
|
535
|
+
retry(),
|
|
536
|
+
],
|
|
537
|
+
|
|
538
|
+
async handler({
|
|
539
|
+
input,
|
|
540
|
+
context,
|
|
541
|
+
}) {
|
|
542
|
+
const api =
|
|
543
|
+
context.container.resolve(
|
|
544
|
+
ApiServiceToken,
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
const user =
|
|
548
|
+
await api.saveUser(input)
|
|
549
|
+
|
|
550
|
+
globalEvents.$emit(
|
|
551
|
+
'user:saved',
|
|
552
|
+
user,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
return {
|
|
556
|
+
success: true,
|
|
557
|
+
user,
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
})
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
# Comparison
|
|
566
|
+
|
|
567
|
+
| Createria | Redux | React Query | react-runtime-pipe |
|
|
568
|
+
| -------------------- | ------ | ------------ | ------------------ |
|
|
569
|
+
| Middleware Pipeline | ✅ | ❌ | ✅ |
|
|
570
|
+
| Dependency Injection | ❌ | ❌ | ✅ |
|
|
571
|
+
| Runtime Context | ❌ | ❌ | ✅ |
|
|
572
|
+
| Event Bus | ❌ | ❌ | ✅ |
|
|
573
|
+
| Orchestration Layer | ❌ | ⚠️ | ✅ |
|
|
574
|
+
| Service Execution | ❌ | ⚠️ | ✅ |
|
|
575
|
+
| Plugin System | ❌ | ❌ | ✅ |
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
# Philosophy
|
|
580
|
+
|
|
581
|
+
```text
|
|
582
|
+
You control:
|
|
583
|
+
- middleware
|
|
584
|
+
- plugins
|
|
585
|
+
- orchestration
|
|
586
|
+
- architecture
|
|
587
|
+
- services
|
|
588
|
+
|
|
589
|
+
react-runtime-pipe controls:
|
|
590
|
+
- execution
|
|
591
|
+
- lifecycle
|
|
592
|
+
- context
|
|
593
|
+
- typing
|
|
594
|
+
- pipelines
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
# Design
|
|
600
|
+
|
|
601
|
+
- Explicit execution
|
|
602
|
+
- Composition over inheritance
|
|
603
|
+
- Backend-inspired architecture
|
|
604
|
+
- Runtime-first design
|
|
605
|
+
- Framework-agnostic core
|
|
606
|
+
- Deterministic orchestration
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
# When to Use
|
|
611
|
+
|
|
612
|
+
- Large React applications
|
|
613
|
+
- Complex async flows
|
|
614
|
+
- Plugin-based frontends
|
|
615
|
+
- Runtime orchestration systems
|
|
616
|
+
- Event-driven UI architecture
|
|
617
|
+
- Shared business execution layers
|
|
618
|
+
- Backend/frontend unified patterns
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
# License
|
|
623
|
+
|
|
624
|
+
MIT
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Middleware, RuntimeContext } from './types';
|
|
2
|
+
export declare function composeMiddleware<TInput, TResult>(params: {
|
|
3
|
+
middleware: Middleware<TInput, TResult>[];
|
|
4
|
+
execute(): Promise<TResult>;
|
|
5
|
+
input: TInput;
|
|
6
|
+
context: RuntimeContext;
|
|
7
|
+
}): Promise<TResult>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createEventBus } from 'eventbus-z';
|
|
2
|
+
import type { RuntimeContainer } from '../app/container';
|
|
3
|
+
export type RuntimeLogger = {
|
|
4
|
+
info(message: string, meta?: unknown): void;
|
|
5
|
+
error(message: string, meta?: unknown): void;
|
|
6
|
+
};
|
|
7
|
+
export type RuntimeEventBus = ReturnType<typeof createEventBus>;
|
|
8
|
+
export type GlobalEventBus = ReturnType<typeof createEventBus>;
|
|
9
|
+
export type TypedRuntimeEventBus<TEvents extends Record<string, any[]>> = {
|
|
10
|
+
$emit<K extends keyof TEvents>(event: K, ...args: TEvents[K]): void;
|
|
11
|
+
$on<K extends keyof TEvents>(event: K, listener: (...args: TEvents[K]) => void): () => void;
|
|
12
|
+
$off?<K extends keyof TEvents>(event: K, listener?: (...args: TEvents[K]) => void): void;
|
|
13
|
+
$once?<K extends keyof TEvents>(event: K, listener: (...args: TEvents[K]) => void): void;
|
|
14
|
+
};
|
|
15
|
+
export type RuntimeContext = {
|
|
16
|
+
requestId: string;
|
|
17
|
+
signal: AbortSignal;
|
|
18
|
+
container: RuntimeContainer;
|
|
19
|
+
logger: RuntimeLogger;
|
|
20
|
+
events: RuntimeEventBus;
|
|
21
|
+
globalEvents?: GlobalEventBus;
|
|
22
|
+
cache: Map<string, unknown>;
|
|
23
|
+
auth?: {
|
|
24
|
+
userId: string;
|
|
25
|
+
};
|
|
26
|
+
throwIfAborted(): void;
|
|
27
|
+
};
|
|
28
|
+
export type ActionNext<TResult> = () => Promise<TResult>;
|
|
29
|
+
export type Middleware<TInput = unknown, TResult = unknown> = (params: {
|
|
30
|
+
input: TInput;
|
|
31
|
+
context: RuntimeContext;
|
|
32
|
+
next: ActionNext<TResult>;
|
|
33
|
+
}) => Promise<TResult>;
|
|
34
|
+
export type ActionHandler<TInput, TResult> = (params: {
|
|
35
|
+
input: TInput;
|
|
36
|
+
context: RuntimeContext;
|
|
37
|
+
}) => Promise<TResult>;
|
|
38
|
+
export type RuntimeAction<TInput, TResult> = {
|
|
39
|
+
name: string;
|
|
40
|
+
middleware?: Middleware<TInput, TResult>[];
|
|
41
|
+
retry?: {
|
|
42
|
+
attempts: number;
|
|
43
|
+
};
|
|
44
|
+
handler: ActionHandler<TInput, TResult>;
|
|
45
|
+
};
|
|
46
|
+
export type RuntimeSystem = {
|
|
47
|
+
globalEvents: GlobalEventBus;
|
|
48
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function createRequestId(): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type Token<T = any> = symbol & {
|
|
2
|
+
__type?: T;
|
|
3
|
+
};
|
|
4
|
+
export declare function createToken<T>(description: string): Token<T>;
|
|
5
|
+
type Scope = 'singleton' | 'scoped';
|
|
6
|
+
type Provider<T> = {
|
|
7
|
+
scope?: Scope;
|
|
8
|
+
useValue?: T;
|
|
9
|
+
useFactory?: () => T;
|
|
10
|
+
};
|
|
11
|
+
export type RuntimeContainer = ReturnType<typeof createContainer>;
|
|
12
|
+
export declare function createContainer(): {
|
|
13
|
+
register<T>(token: Token<T>, provider: Provider<T>): void;
|
|
14
|
+
resolve<T>(token: Token<T>): T;
|
|
15
|
+
has<T>(token: Token<T>): boolean;
|
|
16
|
+
};
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createContainer, type Token } from './container';
|
|
2
|
+
import type { Middleware, RuntimeContext } from '../action/types';
|
|
3
|
+
import type { Plugin } from './plugin';
|
|
4
|
+
export type ServiceProvider<T = any> = {
|
|
5
|
+
token: Token<T>;
|
|
6
|
+
useValue: T;
|
|
7
|
+
};
|
|
8
|
+
export type CreateAppOptions = {
|
|
9
|
+
services?: ServiceProvider[];
|
|
10
|
+
};
|
|
11
|
+
export type RuntimeApp = {
|
|
12
|
+
container: ReturnType<typeof createContainer>;
|
|
13
|
+
readonly plugins: Plugin[];
|
|
14
|
+
readonly middlewares: Middleware[];
|
|
15
|
+
initialized: boolean;
|
|
16
|
+
createContext(): Promise<Partial<RuntimeContext>>;
|
|
17
|
+
plugin(plugin: Plugin): RuntimeApp;
|
|
18
|
+
use(middleware: Middleware): RuntimeApp;
|
|
19
|
+
run(): Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
export declare function createApp(options?: CreateAppOptions): RuntimeApp;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RuntimeContext } from '../action/types';
|
|
2
|
+
import type { RuntimeApp } from './createApp';
|
|
3
|
+
export type PluginContext = {
|
|
4
|
+
app: RuntimeApp;
|
|
5
|
+
};
|
|
6
|
+
export type Plugin = {
|
|
7
|
+
name: string;
|
|
8
|
+
setup?(context: PluginContext): void;
|
|
9
|
+
createContext?(): Promise<Partial<RuntimeContext>>;
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";function e(){const e=new Map,t=new Map;return{register(n,r){t.set(n,r),r.useValue&&e.set(n,r.useValue)},resolve(n){if(e.has(n))return e.get(n);const r=t.get(n);if(!r)throw Error("Missing dependency: "+(n+""));if(r.useFactory){const t=r.useFactory();return"scoped"!==r.scope&&e.set(n,t),t}return r.useValue},has:e=>t.has(e)}}async function t(e){const{middleware:t,execute:n,input:r,context:o}=e;return async function e(c){const s=t[c];if(!s)return n();let i=!1;return s({input:r,context:o,next:async()=>{if(i)throw Error("next() already called");return i=!0,e(c+1)}})}(0)}exports.createApp=function(t={}){const n=e(),r=[],o=[];for(const e of t.services??[])n.register(e.token,{useValue:e.useValue});const c={container:n,plugins:r,middlewares:o,initialized:!1,async createContext(){let e={};for(const t of r){if(!t.createContext)continue;const n=await t.createContext();e={...e,...n}}return e},plugin:e=>(r.push(e),c),use:e=>(o.push(e),c),async run(){if(!c.initialized){c.initialized=!0;for(const e of r)await(e.setup?.({app:c}))}}};return c},exports.createContainer=e,exports.createRequestId=function(){return Array.from({length:16},()=>Math.floor(16*Math.random()).toString(16)).join("")},exports.createToken=function(e){return Symbol(e)},exports.defineAction=function(e){return e},exports.executeAction=async function(e){const{action:n,input:r,context:o}=e,c=n.retry?.attempts??1;let s;for(let e=0;e<c;e++)try{return await t({middleware:n.middleware??[],input:r,context:o,execute:async()=>(o.throwIfAborted(),n.handler({input:r,context:o}))})}catch(e){s=e}throw s};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";function e(){return"undefined"!=typeof performance?performance.now():Date.now()}exports.abortMiddleware=async({context:e,next:t})=>{e.throwIfAborted();const r=await t();return e.throwIfAborted(),r},exports.auth=function(){return async({context:e,next:t})=>{if(!e.auth?.userId)throw Error("Unauthorized");return t()}},exports.cacheMiddleware=async({context:e,next:t})=>{const r=e.requestId,o=e.cache.get(r);if(o)return e.logger.info("[cache:hit]",{key:r}),o;const n=await t();return e.cache.set(r,n),e.logger.info("[cache:set]",{key:r}),n},exports.eventMiddleware=async({context:e,next:t})=>{e.events?.$emit("action:start",{requestId:e.requestId});try{const r=await t();return e.events?.$emit("action:success",{requestId:e.requestId}),r}catch(t){throw e.events?.$emit("action:error",{requestId:e.requestId,error:t}),t}},exports.logger=async({context:e,input:t,next:r})=>{const o="undefined"!=typeof performance?performance.now():Date.now();e.logger.info("[action:start]",{requestId:e.requestId,input:t});try{const t=await r(),n="undefined"!=typeof performance?performance.now():Date.now();return e.logger.info("[action:success]",{requestId:e.requestId,duration:n-o}),t}catch(t){const r="undefined"!=typeof performance?performance.now():Date.now();throw e.logger.error("[action:error]",{requestId:e.requestId,duration:r-o,error:t instanceof Error?t.message:t}),t}},exports.retry=function(e=3){return async({context:t,next:r})=>{let o;for(let n=1;n<=e;n++)try{t.logger.info("[retry:attempt]",{requestId:t.requestId,attempt:n,max:e}),t.throwIfAborted();const o=await r();return n>1&&t.logger.info("[retry:success-after-retry]",{requestId:t.requestId,attempt:n}),o}catch(e){o=e,t.logger.error("[retry:fail]",{requestId:t.requestId,attempt:n,error:e}),t.events.$emit("retry:fail",{requestId:t.requestId,attempt:n,error:e})}throw t.logger.error("[retry:exhausted]",{requestId:t.requestId,attempts:e,error:o}),o}},exports.timing=async({context:t,next:r})=>{const o=e(),n=t.requestId;t.logger.info("[timing:start]",{requestId:n});try{const s=await r(),a=e()-o;return t.logger.info("[timing:success]",{requestId:n,duration:a}),t.events.$emit("timing",{requestId:n,duration:a}),s}catch(r){const s=e()-o;throw t.logger.error("[timing:error]",{requestId:n,duration:s,error:r}),r}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const e={name:"auth",createContext:async()=>({auth:await Promise.resolve({userId:"user_123"})})},t={name:"cache",createContext:async()=>({cache:new Map})},s="__EVENT_BUS_GLOBAL_SCOPE__",o=()=>(()=>{const e=(()=>{if("undefined"!=typeof window){try{if(window.top)return window.top.location.href,window.top}catch(e){}return window}return"undefined"!=typeof globalThis?globalThis:{}})();return e[s]||Object.defineProperty(e,s,{value:{},writable:!1,enumerable:!1,configurable:!1}),e[s]})(),c="evtBusZxp_scopeZxp",n="globalZxp_defaultName",a=(e,t=n)=>{const s=`${c}__${t}`;return e[s]||(e[s]=new Map),e[s]},i="eventBusRegister_"+c,r=(e,t,s)=>{var o;if(!t||"function"!=typeof s.callback)throw Error("Event name and callback are required");const c=null!==(o=s.scopeName)&&void 0!==o?o:n,r=a(e,c);r.has(t)||r.set(t,new Set);const l=r.get(t);s.single&&l.clear(),l.forEach(e=>{e.callback===s.callback&&l.delete(e)}),l.add(Object.assign({},s)),((e,t,s=n)=>{var o;const c=null!==(o=e[i])&&void 0!==o?o:{};c[s]||(c[s]=new Set),c[s].add(t),e[i]=c})(e,t,c)};function l(e,t){let s=-1;const o=c=>{if(c<=s)return;s=c;const i=e._middlewares[c];i?i(t,()=>o(c+1)):((e,t,s=n,...o)=>{const c=a(e,s).get(t);if(!c)return;const i=Date.now();c.forEach(e=>{e.once&&e.emitted||e.timeCached&&e.timeEffect&&i-e.timeEffect<e.timeCached||(e.callback(...o),e.emitted=!0,e.timeEffect=i)})})(e._scope,t.name,t.scopeName,...t.args)};o(0)}class h{constructor(e=o()){this._middlewares=[],this._isBatching=!1,this._queue=[],this.$use=e=>{this._middlewares.push(e)},this.$batch=e=>{this._isBatching=!0;try{e()}finally{this._isBatching=!1,function(e){const t=e._queue;for(let s=0;s<t.length;s++)l(e,t[s]);t.length=0}(this)}},this.$once=(e,t)=>{r(this._scope,e,{callback:t,once:!0})},this.$on=(e,t)=>{r(this._scope,e,{callback:t,single:!0})},this.$onMultiple=(e,t)=>{r(this._scope,e,{callback:t})},this.$onCached=(e,t,s=100)=>{r(this._scope,e,{callback:t,single:!0,timeCached:s})},this.$onCachedMultiple=(e,t,s=100)=>{r(this._scope,e,{callback:t,timeCached:s})},this.$emit=(e,...t)=>{const s={scopeName:n,name:e,args:t};this._isBatching?this._queue.push(s):l(this,s)},this.$off=(e,t)=>{const s=a(this._scope,n),o=s.get(e);o&&(t?o.forEach(e=>{e.callback===t&&o.delete(e)}):s.delete(e))},this.$offAll=e=>{a(this._scope,n).delete(e)},this.$destroy=()=>{!function(e){Object.keys(e).forEach(t=>{delete e[t]})}(this._scope)},this.$clearEventAcrossScopes=e=>{const t=(()=>{var e;return null!==(e=this._scope[i])&&void 0!==e?e:{}})();Object.keys(t).forEach(s=>{var o;(null===(o=t[s])||void 0===o?void 0:o.has(e))&&a(this._scope,s).delete(e)})},this.$scopeEmit=(e,t,...s)=>{const o={scopeName:e,name:t,args:s};this._isBatching?this._queue.push(o):l(this,o)},this.$scopeOn=(e,t,s)=>{r(this._scope,t,{callback:s,single:!0,scopeName:e})},this.$scopeOnce=(e,t,s)=>{r(this._scope,t,{callback:s,once:!0,scopeName:e})},this.$scopeOnMultiple=(e,t,s)=>{r(this._scope,t,{callback:s,scopeName:e})},this.$scopeOnCached=(e,t,s,o=100)=>{r(this._scope,t,{callback:s,single:!0,timeCached:o,scopeName:e})},this.$scopeOnCachedMultiple=(e,t,s,o=100)=>{r(this._scope,t,{callback:s,timeCached:o,scopeName:e})},this.$scopeOff=(e,t,s)=>{const o=a(this._scope,e),c=o.get(t);c&&(s?c.forEach(e=>{e.callback===s&&c.delete(e)}):o.delete(t))},this.$scopeOffAll=(e,t)=>{a(this._scope,e).delete(t)},this._scope=e}}function p(e,t){return new h(Object.create(null))}const u=p(),{$once:d,$on:f,$off:g,$offAll:m,$emit:$,$onCached:_,$onMultiple:b,$onCachedMultiple:w,$clearEventAcrossScopes:v,$destroy:y,$scopeOn:E,$scopeOnce:x,$scopeOnMultiple:O,$scopeOnCached:C,$scopeOnCachedMultiple:k,$scopeEmit:q,$scopeOff:I,$scopeOffAll:N}=u,M=p(),B={name:"event",createContext:async()=>({globalEvents:M}),setup({app:e}){e.use(async({context:e,next:t})=>{const s=e.globalEvents;s.$emit("action:start",{requestId:e.requestId});try{const o=await t();return s.$emit("action:success",{requestId:e.requestId}),o}catch(t){throw s.$emit("action:error",{requestId:e.requestId,error:t}),t}})}},P={name:"logger",createContext:async()=>({logger:{info:console.log,error:console.error}})},A={name:"logger",setup({app:e}){e.use(async({context:e,next:t})=>{const s="undefined"!=typeof performance?performance.now():Date.now(),o=e.requestId;e.logger.info("[action:start]",{requestId:o});const c=e.globalEvents;c?.$emit?.("action:start",{requestId:o});try{const n=await t(),a="undefined"!=typeof performance?performance.now():Date.now();return e.logger.info("[action:success]",{requestId:o,duration:a-s}),c?.$emit?.("action:success",{requestId:o,duration:a-s}),n}catch(t){const n="undefined"!=typeof performance?performance.now():Date.now();throw e.logger.error("[action:error]",{requestId:o,duration:n-s,error:t}),c?.$emit?.("action:error",{requestId:o,error:t}),t}})},createContext:async()=>({logger:{info:console.log,error:console.error},globalEvents:p()})};exports.authPlugin=e,exports.cachePlugin=t,exports.devtoolsPlugin=e=>({name:"devtools",setup({app:t}){e.enabled&&t.use(async({context:e,next:t})=>await t())}}),exports.eventPlugin=B,exports.globalEvents=M,exports.loggerAdvancePlugin=A,exports.loggerPlugin=P;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=require("react/jsx-runtime"),t=require("react");const c=t.createContext(null);function n(){const e=t.useContext(c);if(!e)throw Error("Missing RuntimeProvider");return e}const o="__EVENT_BUS_GLOBAL_SCOPE__",s=()=>(()=>{const e=(()=>{if("undefined"!=typeof window){try{if(window.top)return window.top.location.href,window.top}catch(e){}return window}return"undefined"!=typeof globalThis?globalThis:{}})();return e[o]||Object.defineProperty(e,o,{value:{},writable:!1,enumerable:!1,configurable:!1}),e[o]})(),i="evtBusZxp_scopeZxp",r="globalZxp_defaultName",a=(e,t=r)=>{const c=`${i}__${t}`;return e[c]||(e[c]=new Map),e[c]},l="eventBusRegister_"+i,u=(e,t,c)=>{var n;if(!t||"function"!=typeof c.callback)throw Error("Event name and callback are required");const o=null!==(n=c.scopeName)&&void 0!==n?n:r,s=a(e,o);s.has(t)||s.set(t,new Set);const i=s.get(t);c.single&&i.clear(),i.forEach(e=>{e.callback===c.callback&&i.delete(e)}),i.add(Object.assign({},c)),((e,t,c=r)=>{var n;const o=null!==(n=e[l])&&void 0!==n?n:{};o[c]||(o[c]=new Set),o[c].add(t),e[l]=o})(e,t,o)};function h(e,t){let c=-1;const n=o=>{if(o<=c)return;c=o;const s=e._middlewares[o];s?s(t,()=>n(o+1)):((e,t,c=r,...n)=>{const o=a(e,c).get(t);if(!o)return;const s=Date.now();o.forEach(e=>{e.once&&e.emitted||e.timeCached&&e.timeEffect&&s-e.timeEffect<e.timeCached||(e.callback(...n),e.emitted=!0,e.timeEffect=s)})})(e._scope,t.name,t.scopeName,...t.args)};n(0)}class p{constructor(e=s()){this._middlewares=[],this._isBatching=!1,this._queue=[],this.$use=e=>{this._middlewares.push(e)},this.$batch=e=>{this._isBatching=!0;try{e()}finally{this._isBatching=!1,function(e){const t=e._queue;for(let c=0;c<t.length;c++)h(e,t[c]);t.length=0}(this)}},this.$once=(e,t)=>{u(this._scope,e,{callback:t,once:!0})},this.$on=(e,t)=>{u(this._scope,e,{callback:t,single:!0})},this.$onMultiple=(e,t)=>{u(this._scope,e,{callback:t})},this.$onCached=(e,t,c=100)=>{u(this._scope,e,{callback:t,single:!0,timeCached:c})},this.$onCachedMultiple=(e,t,c=100)=>{u(this._scope,e,{callback:t,timeCached:c})},this.$emit=(e,...t)=>{const c={scopeName:r,name:e,args:t};this._isBatching?this._queue.push(c):h(this,c)},this.$off=(e,t)=>{const c=a(this._scope,r),n=c.get(e);n&&(t?n.forEach(e=>{e.callback===t&&n.delete(e)}):c.delete(e))},this.$offAll=e=>{a(this._scope,r).delete(e)},this.$destroy=()=>{!function(e){Object.keys(e).forEach(t=>{delete e[t]})}(this._scope)},this.$clearEventAcrossScopes=e=>{const t=(()=>{var e;return null!==(e=this._scope[l])&&void 0!==e?e:{}})();Object.keys(t).forEach(c=>{var n;(null===(n=t[c])||void 0===n?void 0:n.has(e))&&a(this._scope,c).delete(e)})},this.$scopeEmit=(e,t,...c)=>{const n={scopeName:e,name:t,args:c};this._isBatching?this._queue.push(n):h(this,n)},this.$scopeOn=(e,t,c)=>{u(this._scope,t,{callback:c,single:!0,scopeName:e})},this.$scopeOnce=(e,t,c)=>{u(this._scope,t,{callback:c,once:!0,scopeName:e})},this.$scopeOnMultiple=(e,t,c)=>{u(this._scope,t,{callback:c,scopeName:e})},this.$scopeOnCached=(e,t,c,n=100)=>{u(this._scope,t,{callback:c,single:!0,timeCached:n,scopeName:e})},this.$scopeOnCachedMultiple=(e,t,c,n=100)=>{u(this._scope,t,{callback:c,timeCached:n,scopeName:e})},this.$scopeOff=(e,t,c)=>{const n=a(this._scope,e),o=n.get(t);o&&(c?o.forEach(e=>{e.callback===c&&o.delete(e)}):n.delete(t))},this.$scopeOffAll=(e,t)=>{a(this._scope,e).delete(t)},this._scope=e}}function d(e,t){return new p(Object.create(null))}const f=d(),{$once:m,$on:_,$off:$,$offAll:b,$emit:g,$onCached:w,$onMultiple:v,$onCachedMultiple:x,$clearEventAcrossScopes:E,$destroy:y,$scopeOn:O,$scopeOnce:k,$scopeOnMultiple:C,$scopeOnCached:M,$scopeOnCachedMultiple:A,$scopeEmit:N,$scopeOff:B,$scopeOffAll:S}=f;async function j(e){const{middleware:t,execute:c,input:n,context:o}=e;return async function e(s){const i=t[s];if(!i)return c();let r=!1;return i({input:n,context:o,next:async()=>{if(r)throw Error("next() already called");return r=!0,e(s+1)}})}(0)}const q=d();exports.RuntimeProvider=function(t){return e.jsx(c.Provider,{value:t.app,children:t.children})},exports.globalEvents=q,exports.useAction=function(e){const c=n(),o=t.useRef(null),[s,i]=t.useState(!1),[r,a]=t.useState(null),l=t.useMemo(()=>d(),[]);return{execute:async function(t){i(!0),a(null);const n=new AbortController;o.current=n;try{const o=await(c.createContext?.()),s=await async function(e){const{action:t,input:c,context:n}=e,o=t.retry?.attempts??1;let s;for(let e=0;e<o;e++)try{return await j({middleware:t.middleware??[],input:c,context:n,execute:async()=>(n.throwIfAborted(),t.handler({input:c,context:n}))})}catch(e){s=e}throw s}({action:e,input:t,context:{requestId:Array.from({length:16},()=>Math.floor(16*Math.random()).toString(16)).join(""),signal:n.signal,container:c.container,cache:new Map,events:l,logger:{info:console.log,error:console.error},throwIfAborted(){if(n.signal.aborted)throw Error("Action aborted")},...o}});return s}catch(e){throw a(e),e}finally{i(!1),o.current=null}},abort:function(){o.current?.abort()},loading:s,error:r,events:l,globalEvents:q}},exports.useEventBus=function(e,c,n=f){const o=t.useRef(c);o.current=c,t.useEffect(()=>{const t=(...e)=>{o.current(...e)};return n.$on(e,t),()=>{n.$off(e,t)}},[e,n])},exports.useRuntime=n,exports.useService=function(e){return n().container.resolve(e)};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function t(t){return Symbol(t)}function e(){const t=new Map,e=new Map;return{register(n,r){e.set(n,r),r.useValue&&t.set(n,r.useValue)},resolve(n){if(t.has(n))return t.get(n);const r=e.get(n);if(!r)throw Error("Missing dependency: "+(n+""));if(r.useFactory){const e=r.useFactory();return"scoped"!==r.scope&&t.set(n,e),e}return r.useValue},has:t=>e.has(t)}}function n(t={}){const n=e(),r=[],o=[];for(const e of t.services??[])n.register(e.token,{useValue:e.useValue});const i={container:n,plugins:r,middlewares:o,initialized:!1,async createContext(){let t={};for(const e of r){if(!e.createContext)continue;const n=await e.createContext();t={...t,...n}}return t},plugin:t=>(r.push(t),i),use:t=>(o.push(t),i),async run(){if(!i.initialized){i.initialized=!0;for(const t of r)await(t.setup?.({app:i}))}}};return i}function r(t){return t}async function o(t){const{middleware:e,execute:n,input:r,context:o}=t;return async function t(i){const c=e[i];if(!c)return n();let u=!1;return c({input:r,context:o,next:async()=>{if(u)throw Error("next() already called");return u=!0,t(i+1)}})}(0)}async function i(t){const{action:e,input:n,context:r}=t,i=e.retry?.attempts??1;let c;for(let t=0;t<i;t++)try{return await o({middleware:e.middleware??[],input:n,context:r,execute:async()=>(r.throwIfAborted(),e.handler({input:n,context:r}))})}catch(t){c=t}throw c}function c(){return Array.from({length:16},()=>Math.floor(16*Math.random()).toString(16)).join("")}export{n as createApp,e as createContainer,c as createRequestId,t as createToken,r as defineAction,i as executeAction};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=async({context:e,next:t})=>{e.throwIfAborted();const r=await t();return e.throwIfAborted(),r};function t(){return async({context:e,next:t})=>{if(!e.auth?.userId)throw Error("Unauthorized");return t()}}const r=async({context:e,next:t})=>{const r=e.requestId,n=e.cache.get(r);if(n)return e.logger.info("[cache:hit]",{key:r}),n;const o=await t();return e.cache.set(r,o),e.logger.info("[cache:set]",{key:r}),o},n=async({context:e,next:t})=>{e.events?.$emit("action:start",{requestId:e.requestId});try{const r=await t();return e.events?.$emit("action:success",{requestId:e.requestId}),r}catch(t){throw e.events?.$emit("action:error",{requestId:e.requestId,error:t}),t}},o=async({context:e,input:t,next:r})=>{const n="undefined"!=typeof performance?performance.now():Date.now();e.logger.info("[action:start]",{requestId:e.requestId,input:t});try{const t=await r(),o="undefined"!=typeof performance?performance.now():Date.now();return e.logger.info("[action:success]",{requestId:e.requestId,duration:o-n}),t}catch(t){const r="undefined"!=typeof performance?performance.now():Date.now();throw e.logger.error("[action:error]",{requestId:e.requestId,duration:r-n,error:t instanceof Error?t.message:t}),t}};function s(e=3){return async({context:t,next:r})=>{let n;for(let o=1;o<=e;o++)try{t.logger.info("[retry:attempt]",{requestId:t.requestId,attempt:o,max:e}),t.throwIfAborted();const n=await r();return o>1&&t.logger.info("[retry:success-after-retry]",{requestId:t.requestId,attempt:o}),n}catch(e){n=e,t.logger.error("[retry:fail]",{requestId:t.requestId,attempt:o,error:e}),t.events.$emit("retry:fail",{requestId:t.requestId,attempt:o,error:e})}throw t.logger.error("[retry:exhausted]",{requestId:t.requestId,attempts:e,error:n}),n}}function c(){return"undefined"!=typeof performance?performance.now():Date.now()}const a=async({context:e,next:t})=>{const r=c(),n=e.requestId;e.logger.info("[timing:start]",{requestId:n});try{const o=await t(),s=c()-r;return e.logger.info("[timing:success]",{requestId:n,duration:s}),e.events.$emit("timing",{requestId:n,duration:s}),o}catch(t){const o=c()-r;throw e.logger.error("[timing:error]",{requestId:n,duration:o,error:t}),t}};export{e as abortMiddleware,t as auth,r as cacheMiddleware,n as eventMiddleware,o as logger,s as retry,a as timing};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={name:"auth",createContext:async()=>({auth:await Promise.resolve({userId:"user_123"})})},t={name:"cache",createContext:async()=>({cache:new Map})},s=e=>({name:"devtools",setup({app:t}){e.enabled&&t.use(async({context:e,next:t})=>await t())}}),c="__EVENT_BUS_GLOBAL_SCOPE__",o=()=>(()=>{const e=(()=>{if("undefined"!=typeof window){try{if(window.top)return window.top.location.href,window.top}catch(e){}return window}return"undefined"!=typeof globalThis?globalThis:{}})();return e[c]||Object.defineProperty(e,c,{value:{},writable:!1,enumerable:!1,configurable:!1}),e[c]})(),n="evtBusZxp_scopeZxp",a="globalZxp_defaultName",i=(e,t=a)=>{const s=`${n}__${t}`;return e[s]||(e[s]=new Map),e[s]},r="eventBusRegister_"+n,l=(e,t,s)=>{var c;if(!t||"function"!=typeof s.callback)throw Error("Event name and callback are required");const o=null!==(c=s.scopeName)&&void 0!==c?c:a,n=i(e,o);n.has(t)||n.set(t,new Set);const l=n.get(t);s.single&&l.clear(),l.forEach(e=>{e.callback===s.callback&&l.delete(e)}),l.add(Object.assign({},s)),((e,t,s=a)=>{var c;const o=null!==(c=e[r])&&void 0!==c?c:{};o[s]||(o[s]=new Set),o[s].add(t),e[r]=o})(e,t,o)};function h(e,t){let s=-1;const c=o=>{if(o<=s)return;s=o;const n=e._middlewares[o];n?n(t,()=>c(o+1)):((e,t,s=a,...c)=>{const o=i(e,s).get(t);if(!o)return;const n=Date.now();o.forEach(e=>{e.once&&e.emitted||e.timeCached&&e.timeEffect&&n-e.timeEffect<e.timeCached||(e.callback(...c),e.emitted=!0,e.timeEffect=n)})})(e._scope,t.name,t.scopeName,...t.args)};c(0)}class p{constructor(e=o()){this._middlewares=[],this._isBatching=!1,this._queue=[],this.$use=e=>{this._middlewares.push(e)},this.$batch=e=>{this._isBatching=!0;try{e()}finally{this._isBatching=!1,function(e){const t=e._queue;for(let s=0;s<t.length;s++)h(e,t[s]);t.length=0}(this)}},this.$once=(e,t)=>{l(this._scope,e,{callback:t,once:!0})},this.$on=(e,t)=>{l(this._scope,e,{callback:t,single:!0})},this.$onMultiple=(e,t)=>{l(this._scope,e,{callback:t})},this.$onCached=(e,t,s=100)=>{l(this._scope,e,{callback:t,single:!0,timeCached:s})},this.$onCachedMultiple=(e,t,s=100)=>{l(this._scope,e,{callback:t,timeCached:s})},this.$emit=(e,...t)=>{const s={scopeName:a,name:e,args:t};this._isBatching?this._queue.push(s):h(this,s)},this.$off=(e,t)=>{const s=i(this._scope,a),c=s.get(e);c&&(t?c.forEach(e=>{e.callback===t&&c.delete(e)}):s.delete(e))},this.$offAll=e=>{i(this._scope,a).delete(e)},this.$destroy=()=>{!function(e){Object.keys(e).forEach(t=>{delete e[t]})}(this._scope)},this.$clearEventAcrossScopes=e=>{const t=(()=>{var e;return null!==(e=this._scope[r])&&void 0!==e?e:{}})();Object.keys(t).forEach(s=>{var c;(null===(c=t[s])||void 0===c?void 0:c.has(e))&&i(this._scope,s).delete(e)})},this.$scopeEmit=(e,t,...s)=>{const c={scopeName:e,name:t,args:s};this._isBatching?this._queue.push(c):h(this,c)},this.$scopeOn=(e,t,s)=>{l(this._scope,t,{callback:s,single:!0,scopeName:e})},this.$scopeOnce=(e,t,s)=>{l(this._scope,t,{callback:s,once:!0,scopeName:e})},this.$scopeOnMultiple=(e,t,s)=>{l(this._scope,t,{callback:s,scopeName:e})},this.$scopeOnCached=(e,t,s,c=100)=>{l(this._scope,t,{callback:s,single:!0,timeCached:c,scopeName:e})},this.$scopeOnCachedMultiple=(e,t,s,c=100)=>{l(this._scope,t,{callback:s,timeCached:c,scopeName:e})},this.$scopeOff=(e,t,s)=>{const c=i(this._scope,e),o=c.get(t);o&&(s?o.forEach(e=>{e.callback===s&&o.delete(e)}):c.delete(t))},this.$scopeOffAll=(e,t)=>{i(this._scope,e).delete(t)},this._scope=e}}function u(e,t){return new p(Object.create(null))}const d=u(),{$once:f,$on:m,$off:$,$offAll:g,$emit:_,$onCached:b,$onMultiple:w,$onCachedMultiple:y,$clearEventAcrossScopes:v,$destroy:E,$scopeOn:O,$scopeOnce:C,$scopeOnMultiple:k,$scopeOnCached:q,$scopeOnCachedMultiple:x,$scopeEmit:I,$scopeOff:N,$scopeOffAll:M}=d,B=u(),A={name:"event",createContext:async()=>({globalEvents:B}),setup({app:e}){e.use(async({context:e,next:t})=>{const s=e.globalEvents;s.$emit("action:start",{requestId:e.requestId});try{const c=await t();return s.$emit("action:success",{requestId:e.requestId}),c}catch(t){throw s.$emit("action:error",{requestId:e.requestId,error:t}),t}})}},S={name:"logger",createContext:async()=>({logger:{info:console.log,error:console.error}})},j={name:"logger",setup({app:e}){e.use(async({context:e,next:t})=>{const s="undefined"!=typeof performance?performance.now():Date.now(),c=e.requestId;e.logger.info("[action:start]",{requestId:c});const o=e.globalEvents;o?.$emit?.("action:start",{requestId:c});try{const n=await t(),a="undefined"!=typeof performance?performance.now():Date.now();return e.logger.info("[action:success]",{requestId:c,duration:a-s}),o?.$emit?.("action:success",{requestId:c,duration:a-s}),n}catch(t){const n="undefined"!=typeof performance?performance.now():Date.now();throw e.logger.error("[action:error]",{requestId:c,duration:n-s,error:t}),o?.$emit?.("action:error",{requestId:c,error:t}),t}})},createContext:async()=>({logger:{info:console.log,error:console.error},globalEvents:u()})};export{e as authPlugin,t as cachePlugin,s as devtoolsPlugin,A as eventPlugin,B as globalEvents,j as loggerAdvancePlugin,S as loggerPlugin};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import t,{createContext as c,useContext as n,useRef as o,useState as s,useMemo as i}from"react";const r=c(null);function a(t){return e(r.Provider,{value:t.app,children:t.children})}function l(){const e=n(r);if(!e)throw Error("Missing RuntimeProvider");return e}const h="__EVENT_BUS_GLOBAL_SCOPE__",u=()=>(()=>{const e=(()=>{if("undefined"!=typeof window){try{if(window.top)return window.top.location.href,window.top}catch(e){}return window}return"undefined"!=typeof globalThis?globalThis:{}})();return e[h]||Object.defineProperty(e,h,{value:{},writable:!1,enumerable:!1,configurable:!1}),e[h]})(),p="evtBusZxp_scopeZxp",f="globalZxp_defaultName",d=(e,t=f)=>{const c=`${p}__${t}`;return e[c]||(e[c]=new Map),e[c]},m="eventBusRegister_"+p,_=(e,t,c)=>{var n;if(!t||"function"!=typeof c.callback)throw Error("Event name and callback are required");const o=null!==(n=c.scopeName)&&void 0!==n?n:f,s=d(e,o);s.has(t)||s.set(t,new Set);const i=s.get(t);c.single&&i.clear(),i.forEach(e=>{e.callback===c.callback&&i.delete(e)}),i.add(Object.assign({},c)),((e,t,c=f)=>{var n;const o=null!==(n=e[m])&&void 0!==n?n:{};o[c]||(o[c]=new Set),o[c].add(t),e[m]=o})(e,t,o)};function $(e,t){let c=-1;const n=o=>{if(o<=c)return;c=o;const s=e._middlewares[o];s?s(t,()=>n(o+1)):((e,t,c=f,...n)=>{const o=d(e,c).get(t);if(!o)return;const s=Date.now();o.forEach(e=>{e.once&&e.emitted||e.timeCached&&e.timeEffect&&s-e.timeEffect<e.timeCached||(e.callback(...n),e.emitted=!0,e.timeEffect=s)})})(e._scope,t.name,t.scopeName,...t.args)};n(0)}class b{constructor(e=u()){this._middlewares=[],this._isBatching=!1,this._queue=[],this.$use=e=>{this._middlewares.push(e)},this.$batch=e=>{this._isBatching=!0;try{e()}finally{this._isBatching=!1,function(e){const t=e._queue;for(let c=0;c<t.length;c++)$(e,t[c]);t.length=0}(this)}},this.$once=(e,t)=>{_(this._scope,e,{callback:t,once:!0})},this.$on=(e,t)=>{_(this._scope,e,{callback:t,single:!0})},this.$onMultiple=(e,t)=>{_(this._scope,e,{callback:t})},this.$onCached=(e,t,c=100)=>{_(this._scope,e,{callback:t,single:!0,timeCached:c})},this.$onCachedMultiple=(e,t,c=100)=>{_(this._scope,e,{callback:t,timeCached:c})},this.$emit=(e,...t)=>{const c={scopeName:f,name:e,args:t};this._isBatching?this._queue.push(c):$(this,c)},this.$off=(e,t)=>{const c=d(this._scope,f),n=c.get(e);n&&(t?n.forEach(e=>{e.callback===t&&n.delete(e)}):c.delete(e))},this.$offAll=e=>{d(this._scope,f).delete(e)},this.$destroy=()=>{!function(e){Object.keys(e).forEach(t=>{delete e[t]})}(this._scope)},this.$clearEventAcrossScopes=e=>{const t=(()=>{var e;return null!==(e=this._scope[m])&&void 0!==e?e:{}})();Object.keys(t).forEach(c=>{var n;(null===(n=t[c])||void 0===n?void 0:n.has(e))&&d(this._scope,c).delete(e)})},this.$scopeEmit=(e,t,...c)=>{const n={scopeName:e,name:t,args:c};this._isBatching?this._queue.push(n):$(this,n)},this.$scopeOn=(e,t,c)=>{_(this._scope,t,{callback:c,single:!0,scopeName:e})},this.$scopeOnce=(e,t,c)=>{_(this._scope,t,{callback:c,once:!0,scopeName:e})},this.$scopeOnMultiple=(e,t,c)=>{_(this._scope,t,{callback:c,scopeName:e})},this.$scopeOnCached=(e,t,c,n=100)=>{_(this._scope,t,{callback:c,single:!0,timeCached:n,scopeName:e})},this.$scopeOnCachedMultiple=(e,t,c,n=100)=>{_(this._scope,t,{callback:c,timeCached:n,scopeName:e})},this.$scopeOff=(e,t,c)=>{const n=d(this._scope,e),o=n.get(t);o&&(c?o.forEach(e=>{e.callback===c&&o.delete(e)}):n.delete(t))},this.$scopeOffAll=(e,t)=>{d(this._scope,e).delete(t)},this._scope=e}}function g(e,t){return new b(Object.create(null))}const w=g(),{$once:y,$on:E,$off:v,$offAll:O,$emit:k,$onCached:x,$onMultiple:C,$onCachedMultiple:M,$clearEventAcrossScopes:A,$destroy:N,$scopeOn:B,$scopeOnce:j,$scopeOnMultiple:S,$scopeOnCached:q,$scopeOnCachedMultiple:P,$scopeEmit:I,$scopeOff:R,$scopeOffAll:T}=w;async function Z(e){const{middleware:t,execute:c,input:n,context:o}=e;return async function e(s){const i=t[s];if(!i)return c();let r=!1;return i({input:n,context:o,next:async()=>{if(r)throw Error("next() already called");return r=!0,e(s+1)}})}(0)}const L=g();function D(e){const t=l(),c=o(null),[n,r]=s(!1),[a,h]=s(null),u=i(()=>g(),[]);return{execute:async function(n){r(!0),h(null);const o=new AbortController;c.current=o;try{const c=await(t.createContext?.()),s=await async function(e){const{action:t,input:c,context:n}=e,o=t.retry?.attempts??1;let s;for(let e=0;e<o;e++)try{return await Z({middleware:t.middleware??[],input:c,context:n,execute:async()=>(n.throwIfAborted(),t.handler({input:c,context:n}))})}catch(e){s=e}throw s}({action:e,input:n,context:{requestId:Array.from({length:16},()=>Math.floor(16*Math.random()).toString(16)).join(""),signal:o.signal,container:t.container,cache:new Map,events:u,logger:{info:console.log,error:console.error},throwIfAborted(){if(o.signal.aborted)throw Error("Action aborted")},...c}});return s}catch(e){throw h(e),e}finally{r(!1),c.current=null}},abort:function(){c.current?.abort()},loading:n,error:a,events:u,globalEvents:L}}function G(e,c,n=w){const o=t.useRef(c);o.current=c,t.useEffect(()=>{const t=(...e)=>{o.current(...e)};return n.$on(e,t),()=>{n.$off(e,t)}},[e,n])}function U(e){return l().container.resolve(e)}export{a as RuntimeProvider,L as globalEvents,D as useAction,G as useEventBus,l as useRuntime,U as useService};
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { RuntimeApp } from '../app/createApp';
|
|
3
|
+
export declare function RuntimeProvider(props: {
|
|
4
|
+
app: RuntimeApp;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function useRuntime(): RuntimeApp;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RuntimeAction } from '../action/types';
|
|
2
|
+
export declare const globalEvents: import("eventbus-z").GlobalBus<Record<string, any[]>, false>;
|
|
3
|
+
export declare function useAction<TInput, TResult>(action: RuntimeAction<TInput, TResult>): {
|
|
4
|
+
execute: (input: TInput) => Promise<TResult>;
|
|
5
|
+
abort: () => void;
|
|
6
|
+
loading: boolean;
|
|
7
|
+
error: unknown;
|
|
8
|
+
events: import("eventbus-z").GlobalBus<Record<string, any[]>, false>;
|
|
9
|
+
globalEvents: import("eventbus-z").GlobalBus<Record<string, any[]>, false>;
|
|
10
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-runtime-pipe",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "A backend-style runtime for React with middleware, DI, plugins, and structured action execution.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Delpi.Kye",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./build/cjs/index.cjs",
|
|
10
|
+
"module": "./build/esm/index.js",
|
|
11
|
+
"types": "./build/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./build/index.d.ts",
|
|
15
|
+
"import": "./build/esm/index.js",
|
|
16
|
+
"require": "./build/cjs/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./react": {
|
|
19
|
+
"types": "./build/react/index.d.ts",
|
|
20
|
+
"import": "./build/esm/react/index.js",
|
|
21
|
+
"require": "./build/cjs/react/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./middleware": {
|
|
24
|
+
"types": "./build/middleware/index.d.ts",
|
|
25
|
+
"import": "./build/esm/middleware/index.js",
|
|
26
|
+
"require": "./build/cjs/middleware/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./plugin": {
|
|
29
|
+
"types": "./build/plugin/index.d.ts",
|
|
30
|
+
"import": "./build/esm/plugin/index.js",
|
|
31
|
+
"require": "./build/cjs/plugin/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"build",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"clean": "rimraf build",
|
|
41
|
+
"build": "rollup -c",
|
|
42
|
+
"cb": "npm run clean && npm run build",
|
|
43
|
+
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
44
|
+
"lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
|
|
45
|
+
"typecheck": "tsc --noEmit",
|
|
46
|
+
"prepublishOnly": "npm run clean && npm run lint && npm run typecheck && npm run build"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/delpikye-v/react-runtime-pipe.git"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/delpikye-v/react-runtime-pipe#readme",
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/delpikye-v/react-runtime-pipe/issues"
|
|
55
|
+
},
|
|
56
|
+
"funding": {
|
|
57
|
+
"type": "github",
|
|
58
|
+
"url": "https://github.com/sponsors/delpikye-v"
|
|
59
|
+
},
|
|
60
|
+
"keywords": [
|
|
61
|
+
"react",
|
|
62
|
+
"react-runtime",
|
|
63
|
+
"runtime",
|
|
64
|
+
"execution-runtime",
|
|
65
|
+
"execution-engine",
|
|
66
|
+
"action-runtime",
|
|
67
|
+
"middleware",
|
|
68
|
+
"middleware-pipeline",
|
|
69
|
+
"plugin-system",
|
|
70
|
+
"dependency-injection",
|
|
71
|
+
"di-container",
|
|
72
|
+
"orchestration",
|
|
73
|
+
"event-driven",
|
|
74
|
+
"event-bus",
|
|
75
|
+
"frontend-runtime",
|
|
76
|
+
"react-architecture",
|
|
77
|
+
"systems-design",
|
|
78
|
+
"runtime-context",
|
|
79
|
+
"typed-actions",
|
|
80
|
+
"typescript",
|
|
81
|
+
"frontend-framework",
|
|
82
|
+
"headless-runtime",
|
|
83
|
+
"async-flow",
|
|
84
|
+
"workflow-engine",
|
|
85
|
+
"react-hooks",
|
|
86
|
+
"react-state",
|
|
87
|
+
"react-patterns",
|
|
88
|
+
"state-orchestration",
|
|
89
|
+
"frontend-infrastructure",
|
|
90
|
+
"app-runtime",
|
|
91
|
+
"framework-agnostic"
|
|
92
|
+
],
|
|
93
|
+
"peerDependencies": {
|
|
94
|
+
"react": "^18 || ^19",
|
|
95
|
+
"react-dom": "^18 || ^19"
|
|
96
|
+
},
|
|
97
|
+
"dependencies": {
|
|
98
|
+
"eventbus-z": "^2.4.0"
|
|
99
|
+
},
|
|
100
|
+
"devDependencies": {
|
|
101
|
+
"@rollup/plugin-commonjs": "^25.0.7",
|
|
102
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
103
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
104
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
105
|
+
"@types/node": "^25.5.0",
|
|
106
|
+
"@types/react": "^18.2.38",
|
|
107
|
+
"@types/react-dom": "^18.2.15",
|
|
108
|
+
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
109
|
+
"@typescript-eslint/parser": "^6.21.0",
|
|
110
|
+
"eslint": "^8.57.1",
|
|
111
|
+
"eslint-config-prettier": "^10.1.8",
|
|
112
|
+
"eslint-plugin-import": "^2.32.0",
|
|
113
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
114
|
+
"eslint-plugin-simple-import-sort": "^13.0.0",
|
|
115
|
+
"eslint-plugin-unused-imports": "^4.4.1",
|
|
116
|
+
"prettier": "^3.8.1",
|
|
117
|
+
"react": "^18.2.0",
|
|
118
|
+
"react-dom": "^18.2.0",
|
|
119
|
+
"rimraf": "^5.0.5",
|
|
120
|
+
"rollup": "^4.12.0",
|
|
121
|
+
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
122
|
+
"rollup-plugin-typescript2": "^0.37.0",
|
|
123
|
+
"tslib": "^2.8.1",
|
|
124
|
+
"tsx": "^4.21.0",
|
|
125
|
+
"typescript": "^5.3.3"
|
|
126
|
+
},
|
|
127
|
+
"engines": {
|
|
128
|
+
"node": ">=18"
|
|
129
|
+
}
|
|
130
|
+
}
|