mycelia-kernel-plugin 1.3.0 → 1.4.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/README.md +49 -8
- package/bin/cli.js +261 -0
- package/package.json +11 -4
- package/src/angular/builders.js +37 -0
- package/src/angular/helpers.js +102 -0
- package/src/angular/index.js +189 -0
- package/src/angular/services.js +32 -0
- package/src/builder/dependency-graph.js +65 -9
- package/src/builder/hook-processor.js +26 -4
- package/src/builder/utils.js +78 -22
- package/src/core/facet.js +16 -3
- package/src/index.js +18 -0
- package/src/manager/facet-manager.js +10 -2
- package/src/qwik/builders.js +39 -0
- package/src/qwik/index.js +178 -0
- package/src/qwik/listeners.js +96 -0
- package/src/qwik/queues.js +87 -0
- package/src/qwik/signals.js +32 -0
- package/src/react/README.md +3 -0
- package/src/solid/README.md +69 -0
- package/src/solid/index.js +387 -0
- package/src/utils/instrumentation.js +204 -0
- package/src/utils/use-base.js +205 -30
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Mycelia Plugin System
|
|
2
2
|
|
|
3
|
-
A sophisticated, **framework-agnostic** plugin system with transaction safety, lifecycle management, and official bindings for React
|
|
3
|
+
A sophisticated, **framework-agnostic** plugin system with transaction safety, lifecycle management, and official bindings for React, Vue 3, Svelte, Angular, Qwik, and Solid.js.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
@@ -15,7 +15,7 @@ Mycelia Plugin System is a **framework-agnostic**, standalone plugin architectur
|
|
|
15
15
|
- **Facet contracts** - Runtime validation of plugin interfaces
|
|
16
16
|
- **Standalone mode** - Works without message system or other dependencies
|
|
17
17
|
- **Built-in hooks** - Ships with `useListeners` for event-driven architectures (see [Simple Event System Example](#simple-event-system-example)), plus `useQueue` and `useSpeak`
|
|
18
|
-
- **Framework bindings** - Official bindings for [React](#react-bindings), [Vue 3](#vue-bindings),
|
|
18
|
+
- **Framework bindings** - Official bindings for [React](#react-bindings), [Vue 3](#vue-bindings), [Svelte](#svelte-bindings), [Angular](#angular-bindings), [Qwik](#qwik-bindings), and [Solid.js](#solidjs-bindings)
|
|
19
19
|
|
|
20
20
|
**Facets** are the concrete runtime capabilities produced by hooks and attached to the system.
|
|
21
21
|
|
|
@@ -26,9 +26,12 @@ The system is designed to be framework-agnostic. Your domain logic lives in Myce
|
|
|
26
26
|
- **React** - Use `MyceliaProvider` and React hooks (`useFacet`, `useListener`)
|
|
27
27
|
- **Vue 3** - Use `MyceliaPlugin` and Vue composables (`useFacet`, `useListener`)
|
|
28
28
|
- **Svelte** - Use `setMyceliaSystem` and Svelte stores (`useFacet`, `useListener`)
|
|
29
|
+
- **Angular** - Use `MyceliaService` and RxJS observables (`useFacet`, `useListener`)
|
|
30
|
+
- **Qwik** - Use `MyceliaProvider` and Qwik signals (`useFacet`, `useListener`)
|
|
31
|
+
- **Solid.js** - Use `MyceliaProvider` and Solid.js signals (`useFacet`, `useListener`)
|
|
29
32
|
- **Vanilla JS/Node.js** - Use the system directly without any framework bindings
|
|
30
33
|
|
|
31
|
-
See the [React Todo App](./examples/react-todo/README.md), [Vue Todo App](./examples/vue-todo/README.md),
|
|
34
|
+
See the [React Todo App](./examples/react-todo/README.md), [Vue Todo App](./examples/vue-todo/README.md), [Svelte Todo App](./examples/svelte-todo/README.md), and [Solid.js Todo App](./examples/solid-todo/README.md) examples - they all use the **exact same plugin code**, demonstrating true framework independence.
|
|
32
35
|
|
|
33
36
|
## Quick Start
|
|
34
37
|
|
|
@@ -75,6 +78,15 @@ const system = await useBase('my-app')
|
|
|
75
78
|
.use(useDatabase)
|
|
76
79
|
.build();
|
|
77
80
|
|
|
81
|
+
// Or configure multiple facets and hooks at once
|
|
82
|
+
const system = await useBase('my-app')
|
|
83
|
+
.configMultiple({
|
|
84
|
+
database: { host: 'localhost', port: 5432 },
|
|
85
|
+
cache: { ttl: 3600 }
|
|
86
|
+
})
|
|
87
|
+
.useMultiple([useDatabase, useCache])
|
|
88
|
+
.build();
|
|
89
|
+
|
|
78
90
|
// Use the plugin
|
|
79
91
|
const db = system.find('database');
|
|
80
92
|
await db.query('SELECT * FROM users');
|
|
@@ -284,7 +296,7 @@ StandalonePluginSystem
|
|
|
284
296
|
|
|
285
297
|
- **`createHook()`** - Create a plugin hook
|
|
286
298
|
- **`createFacetContract()`** - Create a facet contract
|
|
287
|
-
- **`useBase()`** - Fluent API builder for StandalonePluginSystem
|
|
299
|
+
- **`useBase()`** - Fluent API builder for StandalonePluginSystem (see [useBase Documentation](./docs/utils/USE-BASE.md))
|
|
288
300
|
|
|
289
301
|
### Utilities
|
|
290
302
|
|
|
@@ -295,12 +307,19 @@ StandalonePluginSystem
|
|
|
295
307
|
|
|
296
308
|
Comprehensive documentation is available in the [`docs/`](./docs/) directory:
|
|
297
309
|
|
|
310
|
+
### Quick Links
|
|
311
|
+
- **[useBase Guide](./docs/utils/USE-BASE.md)** - Complete guide to the fluent API builder with all methods
|
|
312
|
+
- **[Instrumentation](./docs/instrumentation.md)** - Debugging and performance instrumentation
|
|
313
|
+
|
|
314
|
+
### Full Documentation
|
|
298
315
|
- **[Getting Started Guide](./docs/getting-started/README.md)** - Quick start with examples
|
|
299
316
|
- **[Hooks and Facets Overview](./docs/core-concepts/HOOKS-AND-FACETS-OVERVIEW.md)** - Core concepts
|
|
300
317
|
- **[Built-in Hooks](./docs/hooks/README.md)** - Documentation for `useListeners`, `useQueue`, and `useSpeak`
|
|
301
318
|
- **[React Bindings](./docs/react/README.md)** - React integration utilities (`MyceliaProvider`, `useFacet`, `useListener`)
|
|
302
319
|
- **[Vue Bindings](./docs/vue/README.md)** - Vue 3 integration utilities (`MyceliaPlugin`, `useFacet`, `useListener`) ⭐
|
|
303
320
|
- **[Svelte Bindings](./docs/svelte/README.md)** - Svelte integration utilities (`setMyceliaSystem`, `useFacet`, `useListener`) ⭐
|
|
321
|
+
- **[Angular Bindings](./docs/angular/README.md)** - Angular integration utilities (`MyceliaService`, `useFacet`, `useListener`) ⭐
|
|
322
|
+
- **[Qwik Bindings](./docs/qwik/README.md)** - Qwik integration utilities (`MyceliaProvider`, `useFacet`, `useListener`) ⭐
|
|
304
323
|
- **[Standalone Plugin System](./docs/standalone/STANDALONE-PLUGIN-SYSTEM.md)** - Complete usage guide
|
|
305
324
|
- **[Documentation Index](./docs/README.md)** - Full documentation index
|
|
306
325
|
|
|
@@ -328,12 +347,25 @@ See the `examples/` directory for:
|
|
|
328
347
|
- Composition API integration with reactive state management
|
|
329
348
|
|
|
330
349
|
- **[Svelte Todo App](./examples/svelte-todo/README.md)** ⭐ – A complete Svelte example demonstrating:
|
|
331
|
-
|
|
350
|
+
- **[Solid.js Todo App](./examples/solid-todo/README.md)** ⭐ – A complete Solid.js example demonstrating:
|
|
351
|
+
- **Framework-agnostic plugins** - Uses the same shared plugin code as React, Vue, and Svelte examples
|
|
332
352
|
- Event-driven state synchronization (`todos:changed` events)
|
|
333
|
-
-
|
|
334
|
-
-
|
|
353
|
+
- Solid.js bindings (`MyceliaProvider`, `useFacet`, `useListener`)
|
|
354
|
+
- Signal-based reactivity with automatic updates
|
|
335
355
|
|
|
336
|
-
|
|
356
|
+
- **[Angular Todo App](./examples/angular-todo/README.md)** ⭐ – A complete Angular example demonstrating:
|
|
357
|
+
- **Framework-agnostic plugins** - Uses the same shared plugin code as React, Vue, and Svelte examples
|
|
358
|
+
- Event-driven state synchronization (`todos:changed` events)
|
|
359
|
+
- Angular bindings (`MyceliaService`, `useFacet`, `useListener`)
|
|
360
|
+
- RxJS observables for reactive state management
|
|
361
|
+
|
|
362
|
+
- **[Qwik Todo App](./examples/qwik-todo/README.md)** ⭐ – A complete Qwik example demonstrating:
|
|
363
|
+
- **Framework-agnostic plugins** - Uses the same shared plugin code as all other framework examples
|
|
364
|
+
- Event-driven state synchronization (`todos:changed` events)
|
|
365
|
+
- Qwik bindings (`MyceliaProvider`, `useFacet`, `useListener`)
|
|
366
|
+
- Qwik signals for reactive state management
|
|
367
|
+
|
|
368
|
+
All six examples use the **exact same Mycelia plugin code** from `examples/todo-shared/`, proving that plugins are truly framework-independent. Write your domain logic once, use it everywhere!
|
|
337
369
|
|
|
338
370
|
## CLI Tool
|
|
339
371
|
|
|
@@ -357,6 +389,15 @@ npx mycelia-kernel-plugin init vue my-vue-app
|
|
|
357
389
|
|
|
358
390
|
# Initialize a Svelte project with Mycelia bindings
|
|
359
391
|
npx mycelia-kernel-plugin init svelte my-svelte-app
|
|
392
|
+
|
|
393
|
+
# Initialize an Angular project with Mycelia bindings
|
|
394
|
+
npx mycelia-kernel-plugin init angular my-angular-app
|
|
395
|
+
|
|
396
|
+
# Initialize a Qwik project with Mycelia bindings
|
|
397
|
+
npx mycelia-kernel-plugin init qwik my-qwik-app
|
|
398
|
+
|
|
399
|
+
# Initialize a Solid.js project with Mycelia bindings
|
|
400
|
+
npx mycelia-kernel-plugin init solid my-solid-app
|
|
360
401
|
```
|
|
361
402
|
|
|
362
403
|
Or install globally:
|
package/bin/cli.js
CHANGED
|
@@ -74,6 +74,12 @@ function handleInit(args) {
|
|
|
74
74
|
} else if (args[0] === 'svelte') {
|
|
75
75
|
const projectName = args[1] || 'my-svelte-app';
|
|
76
76
|
initSvelteProject(projectName);
|
|
77
|
+
} else if (args[0] === 'angular') {
|
|
78
|
+
const projectName = args[1] || 'my-angular-app';
|
|
79
|
+
initAngularProject(projectName);
|
|
80
|
+
} else if (args[0] === 'qwik') {
|
|
81
|
+
const projectName = args[1] || 'my-qwik-app';
|
|
82
|
+
initQwikProject(projectName);
|
|
77
83
|
} else {
|
|
78
84
|
const projectName = args[0];
|
|
79
85
|
initProject(projectName);
|
|
@@ -867,6 +873,257 @@ dist/
|
|
|
867
873
|
console.log(` npm run dev`);
|
|
868
874
|
}
|
|
869
875
|
|
|
876
|
+
/**
|
|
877
|
+
* Initialize an Angular project with Mycelia bindings
|
|
878
|
+
*/
|
|
879
|
+
function initAngularProject(projectName) {
|
|
880
|
+
const projectDir = projectName;
|
|
881
|
+
|
|
882
|
+
if (existsSync(projectDir)) {
|
|
883
|
+
console.error(`Error: Directory already exists: ${projectDir}`);
|
|
884
|
+
process.exit(1);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
console.log(`Creating Angular project: ${projectName}...`);
|
|
888
|
+
console.log(`\nNote: This creates a basic Angular project structure.`);
|
|
889
|
+
console.log(`You may need to run 'ng new' separately for a full Angular CLI setup.`);
|
|
890
|
+
|
|
891
|
+
// Create directory structure
|
|
892
|
+
mkdirSync(projectDir, { recursive: true });
|
|
893
|
+
mkdirSync(join(projectDir, 'src'), { recursive: true });
|
|
894
|
+
mkdirSync(join(projectDir, 'src/app'), { recursive: true });
|
|
895
|
+
mkdirSync(join(projectDir, 'src/app/services'), { recursive: true });
|
|
896
|
+
mkdirSync(join(projectDir, 'src/app/components'), { recursive: true });
|
|
897
|
+
mkdirSync(join(projectDir, 'src/mycelia'), { recursive: true });
|
|
898
|
+
|
|
899
|
+
// Create package.json
|
|
900
|
+
const packageJson = {
|
|
901
|
+
name: projectName,
|
|
902
|
+
version: '1.0.0',
|
|
903
|
+
type: 'module',
|
|
904
|
+
scripts: {
|
|
905
|
+
start: 'ng serve',
|
|
906
|
+
build: 'ng build',
|
|
907
|
+
test: 'ng test'
|
|
908
|
+
},
|
|
909
|
+
dependencies: {
|
|
910
|
+
'mycelia-kernel-plugin': '^1.3.0',
|
|
911
|
+
'@angular/core': '^17.0.0',
|
|
912
|
+
'@angular/common': '^17.0.0',
|
|
913
|
+
'rxjs': '^7.8.0'
|
|
914
|
+
},
|
|
915
|
+
devDependencies: {
|
|
916
|
+
'@angular/cli': '^17.0.0',
|
|
917
|
+
'@angular/compiler-cli': '^17.0.0',
|
|
918
|
+
'typescript': '^5.0.0'
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
writeFileSync(
|
|
923
|
+
join(projectDir, 'package.json'),
|
|
924
|
+
JSON.stringify(packageJson, null, 2),
|
|
925
|
+
'utf8'
|
|
926
|
+
);
|
|
927
|
+
|
|
928
|
+
// Create Mycelia service
|
|
929
|
+
const myceliaService = `import { Injectable } from '@angular/core';
|
|
930
|
+
import { BehaviorSubject, Observable } from 'rxjs';
|
|
931
|
+
import { createMyceliaService } from 'mycelia-kernel-plugin/angular';
|
|
932
|
+
import { buildSystem } from '../mycelia/system.builder.js';
|
|
933
|
+
|
|
934
|
+
@Injectable({ providedIn: 'root' })
|
|
935
|
+
export class MyceliaService {
|
|
936
|
+
private service = createMyceliaService(buildSystem);
|
|
937
|
+
|
|
938
|
+
get system$(): Observable<any> {
|
|
939
|
+
return this.service.system$;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
getSystem() {
|
|
943
|
+
return this.service.getSystem();
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
useFacet(kind: string) {
|
|
947
|
+
return this.service.useFacet(kind);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
useListener(eventName: string, handler: Function) {
|
|
951
|
+
return this.service.useListener(eventName, handler);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
async dispose() {
|
|
955
|
+
await this.service.dispose();
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
`;
|
|
959
|
+
|
|
960
|
+
writeFileSync(join(projectDir, 'src/app/services/mycelia.service.ts'), myceliaService, 'utf8');
|
|
961
|
+
|
|
962
|
+
// Create system builder
|
|
963
|
+
const systemBuilder = `import { useBase } from 'mycelia-kernel-plugin';
|
|
964
|
+
import { useListeners } from 'mycelia-kernel-plugin';
|
|
965
|
+
|
|
966
|
+
export async function buildSystem() {
|
|
967
|
+
return useBase('${projectName}')
|
|
968
|
+
.use(useListeners)
|
|
969
|
+
.build();
|
|
970
|
+
}
|
|
971
|
+
`;
|
|
972
|
+
|
|
973
|
+
writeFileSync(join(projectDir, 'src/mycelia/system.builder.ts'), systemBuilder, 'utf8');
|
|
974
|
+
|
|
975
|
+
// Create README
|
|
976
|
+
const readme = `# ${projectName}
|
|
977
|
+
|
|
978
|
+
An Angular application built with Mycelia Plugin System.
|
|
979
|
+
|
|
980
|
+
## Getting Started
|
|
981
|
+
|
|
982
|
+
\`\`\`bash
|
|
983
|
+
npm install
|
|
984
|
+
ng serve
|
|
985
|
+
\`\`\`
|
|
986
|
+
|
|
987
|
+
## Structure
|
|
988
|
+
|
|
989
|
+
- \`src/app/services/\` - Angular services (including MyceliaService)
|
|
990
|
+
- \`src/app/components/\` - Angular components
|
|
991
|
+
- \`src/mycelia/\` - Mycelia plugin code (framework-agnostic)
|
|
992
|
+
`;
|
|
993
|
+
|
|
994
|
+
writeFileSync(join(projectDir, 'README.md'), readme, 'utf8');
|
|
995
|
+
|
|
996
|
+
console.log(`✅ Angular project created: ${projectDir}`);
|
|
997
|
+
console.log(`\nNext steps:`);
|
|
998
|
+
console.log(` cd ${projectDir}`);
|
|
999
|
+
console.log(` npm install`);
|
|
1000
|
+
console.log(` ng serve`);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
/**
|
|
1004
|
+
* Initialize a Qwik project with Mycelia bindings
|
|
1005
|
+
*/
|
|
1006
|
+
function initQwikProject(projectName) {
|
|
1007
|
+
const projectDir = projectName;
|
|
1008
|
+
|
|
1009
|
+
if (existsSync(projectDir)) {
|
|
1010
|
+
console.error(`Error: Directory already exists: ${projectDir}`);
|
|
1011
|
+
process.exit(1);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
console.log(`Creating Qwik project: ${projectName}...`);
|
|
1015
|
+
|
|
1016
|
+
// Create directory structure
|
|
1017
|
+
mkdirSync(projectDir, { recursive: true });
|
|
1018
|
+
mkdirSync(join(projectDir, 'src'), { recursive: true });
|
|
1019
|
+
mkdirSync(join(projectDir, 'src/components'), { recursive: true });
|
|
1020
|
+
mkdirSync(join(projectDir, 'src/mycelia'), { recursive: true });
|
|
1021
|
+
|
|
1022
|
+
// Create package.json
|
|
1023
|
+
const packageJson = {
|
|
1024
|
+
name: projectName,
|
|
1025
|
+
version: '1.0.0',
|
|
1026
|
+
type: 'module',
|
|
1027
|
+
scripts: {
|
|
1028
|
+
dev: 'vite',
|
|
1029
|
+
build: 'vite build',
|
|
1030
|
+
preview: 'vite preview'
|
|
1031
|
+
},
|
|
1032
|
+
dependencies: {
|
|
1033
|
+
'mycelia-kernel-plugin': '^1.3.0',
|
|
1034
|
+
'@builder.io/qwik': '^1.0.0',
|
|
1035
|
+
'@builder.io/qwik-city': '^1.0.0'
|
|
1036
|
+
},
|
|
1037
|
+
devDependencies: {
|
|
1038
|
+
'vite': '^5.0.0',
|
|
1039
|
+
'@vitejs/plugin-qwik': '^1.0.0'
|
|
1040
|
+
}
|
|
1041
|
+
};
|
|
1042
|
+
|
|
1043
|
+
writeFileSync(
|
|
1044
|
+
join(projectDir, 'package.json'),
|
|
1045
|
+
JSON.stringify(packageJson, null, 2),
|
|
1046
|
+
'utf8'
|
|
1047
|
+
);
|
|
1048
|
+
|
|
1049
|
+
// Create App component
|
|
1050
|
+
const appComponent = `import { component$ } from '@builder.io/qwik';
|
|
1051
|
+
import { MyceliaProvider } from 'mycelia-kernel-plugin/qwik';
|
|
1052
|
+
import { buildSystem } from './mycelia/system.builder.js';
|
|
1053
|
+
import { TodoApp } from './components/TodoApp';
|
|
1054
|
+
|
|
1055
|
+
export default component$(() => {
|
|
1056
|
+
return (
|
|
1057
|
+
<MyceliaProvider build={buildSystem}>
|
|
1058
|
+
<TodoApp />
|
|
1059
|
+
</MyceliaProvider>
|
|
1060
|
+
);
|
|
1061
|
+
});
|
|
1062
|
+
`;
|
|
1063
|
+
|
|
1064
|
+
writeFileSync(join(projectDir, 'src/App.tsx'), appComponent, 'utf8');
|
|
1065
|
+
|
|
1066
|
+
// Create system builder
|
|
1067
|
+
const systemBuilder = `import { useBase } from 'mycelia-kernel-plugin';
|
|
1068
|
+
import { useListeners } from 'mycelia-kernel-plugin';
|
|
1069
|
+
|
|
1070
|
+
export async function buildSystem() {
|
|
1071
|
+
return useBase('${projectName}')
|
|
1072
|
+
.use(useListeners)
|
|
1073
|
+
.build();
|
|
1074
|
+
}
|
|
1075
|
+
`;
|
|
1076
|
+
|
|
1077
|
+
writeFileSync(join(projectDir, 'src/mycelia/system.builder.ts'), systemBuilder, 'utf8');
|
|
1078
|
+
|
|
1079
|
+
// Create example component
|
|
1080
|
+
const todoComponent = `import { component$ } from '@builder.io/qwik';
|
|
1081
|
+
import { useFacet, useListener } from 'mycelia-kernel-plugin/qwik';
|
|
1082
|
+
|
|
1083
|
+
export const TodoApp = component$(() => {
|
|
1084
|
+
const todos = useFacet('todos');
|
|
1085
|
+
useListener('todos:changed', (msg) => {
|
|
1086
|
+
console.log('Todos changed:', msg.body);
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
return (
|
|
1090
|
+
<div>
|
|
1091
|
+
<h1>Todo App</h1>
|
|
1092
|
+
{todos.value && <p>Todos facet loaded</p>}
|
|
1093
|
+
</div>
|
|
1094
|
+
);
|
|
1095
|
+
});
|
|
1096
|
+
`;
|
|
1097
|
+
|
|
1098
|
+
writeFileSync(join(projectDir, 'src/components/TodoApp.tsx'), todoComponent, 'utf8');
|
|
1099
|
+
|
|
1100
|
+
// Create README
|
|
1101
|
+
const readme = `# ${projectName}
|
|
1102
|
+
|
|
1103
|
+
A Qwik application built with Mycelia Plugin System.
|
|
1104
|
+
|
|
1105
|
+
## Getting Started
|
|
1106
|
+
|
|
1107
|
+
\`\`\`bash
|
|
1108
|
+
npm install
|
|
1109
|
+
npm run dev
|
|
1110
|
+
\`\`\`
|
|
1111
|
+
|
|
1112
|
+
## Structure
|
|
1113
|
+
|
|
1114
|
+
- \`src/components/\` - Qwik components
|
|
1115
|
+
- \`src/mycelia/\` - Mycelia plugin code (framework-agnostic)
|
|
1116
|
+
`;
|
|
1117
|
+
|
|
1118
|
+
writeFileSync(join(projectDir, 'README.md'), readme, 'utf8');
|
|
1119
|
+
|
|
1120
|
+
console.log(`✅ Qwik project created: ${projectDir}`);
|
|
1121
|
+
console.log(`\nNext steps:`);
|
|
1122
|
+
console.log(` cd ${projectDir}`);
|
|
1123
|
+
console.log(` npm install`);
|
|
1124
|
+
console.log(` npm run dev`);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
870
1127
|
/**
|
|
871
1128
|
* Show help message
|
|
872
1129
|
*/
|
|
@@ -884,6 +1141,8 @@ Commands:
|
|
|
884
1141
|
init react [name] Initialize a React project with Mycelia bindings
|
|
885
1142
|
init vue [name] Initialize a Vue 3 project with Mycelia bindings
|
|
886
1143
|
init svelte [name] Initialize a Svelte project with Mycelia bindings
|
|
1144
|
+
init angular [name] Initialize an Angular project with Mycelia bindings
|
|
1145
|
+
init qwik [name] Initialize a Qwik project with Mycelia bindings
|
|
887
1146
|
help Show this help message
|
|
888
1147
|
|
|
889
1148
|
Examples:
|
|
@@ -893,6 +1152,8 @@ Examples:
|
|
|
893
1152
|
mycelia-kernel-plugin init react my-react-app
|
|
894
1153
|
mycelia-kernel-plugin init vue my-vue-app
|
|
895
1154
|
mycelia-kernel-plugin init svelte my-svelte-app
|
|
1155
|
+
mycelia-kernel-plugin init angular my-angular-app
|
|
1156
|
+
mycelia-kernel-plugin init qwik my-qwik-app
|
|
896
1157
|
|
|
897
1158
|
For more information, visit:
|
|
898
1159
|
https://github.com/lesfleursdelanuitdev/mycelia-kernel-plugin-system
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mycelia-kernel-plugin",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "A sophisticated,
|
|
3
|
+
"version": "1.4.0",
|
|
4
|
+
"description": "A sophisticated, framework-agnostic plugin system with transaction safety, lifecycle management, and official bindings for React, Vue 3, Svelte, Angular, Qwik, and Solid.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": {
|
|
@@ -37,7 +37,10 @@
|
|
|
37
37
|
"./contract/contracts": "./src/contract/contracts/index.js",
|
|
38
38
|
"./react": "./src/react/index.js",
|
|
39
39
|
"./vue": "./src/vue/index.js",
|
|
40
|
-
"./svelte": "./src/svelte/index.js"
|
|
40
|
+
"./svelte": "./src/svelte/index.js",
|
|
41
|
+
"./angular": "./src/angular/index.js",
|
|
42
|
+
"./qwik": "./src/qwik/index.js",
|
|
43
|
+
"./solid": "./src/solid/index.js"
|
|
41
44
|
},
|
|
42
45
|
"files": [
|
|
43
46
|
"src/",
|
|
@@ -56,7 +59,11 @@
|
|
|
56
59
|
"peerDependencies": {
|
|
57
60
|
"react": ">=16.8.0",
|
|
58
61
|
"vue": ">=3.0.0",
|
|
59
|
-
"svelte": ">=3.0.0"
|
|
62
|
+
"svelte": ">=3.0.0",
|
|
63
|
+
"@angular/core": ">=15.0.0",
|
|
64
|
+
"@builder.io/qwik": ">=1.0.0",
|
|
65
|
+
"solid-js": ">=1.0.0",
|
|
66
|
+
"rxjs": ">=7.0.0"
|
|
60
67
|
},
|
|
61
68
|
"devDependencies": {
|
|
62
69
|
"@eslint/js": "^9.36.0",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Angular Builder Helpers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* createAngularSystemBuilder - Create a reusable system builder function for Angular
|
|
7
|
+
*
|
|
8
|
+
* @param {string} name - System name
|
|
9
|
+
* @param {Function} configure - Configuration function: (builder) => builder
|
|
10
|
+
* @returns {Function} Build function: () => Promise<System>
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { useBase } from 'mycelia-kernel-plugin';
|
|
15
|
+
*
|
|
16
|
+
* const buildTodoSystem = createAngularSystemBuilder('todo-app', (b) =>
|
|
17
|
+
* b
|
|
18
|
+
* .config('database', { host: 'localhost' })
|
|
19
|
+
* .use(useDatabase)
|
|
20
|
+
* .use(useListeners)
|
|
21
|
+
* );
|
|
22
|
+
*
|
|
23
|
+
* // Then use in MyceliaModule
|
|
24
|
+
* MyceliaModule.forRoot({ build: buildTodoSystem })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function createAngularSystemBuilder(name, configure) {
|
|
28
|
+
return async function build() {
|
|
29
|
+
// Import useBase - users should have it available
|
|
30
|
+
const { useBase } = await import('../utils/use-base.js');
|
|
31
|
+
let builder = useBase(name);
|
|
32
|
+
builder = configure(builder);
|
|
33
|
+
return builder.build();
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Angular Helper Functions
|
|
3
|
+
*
|
|
4
|
+
* Helper functions for working with Mycelia in Angular components
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* useFacet - Get a facet by kind (for use in Angular components)
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} myceliaService - MyceliaService instance
|
|
11
|
+
* @param {string} kind - Facet kind identifier
|
|
12
|
+
* @returns {import('rxjs').Observable} Observable that emits the facet
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* @Component({...})
|
|
17
|
+
* export class MyComponent {
|
|
18
|
+
* db$ = useFacet(this.mycelia, 'database');
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export function useFacet(myceliaService, kind) {
|
|
23
|
+
const { map } = require('rxjs/operators');
|
|
24
|
+
return myceliaService.system$.pipe(
|
|
25
|
+
map(system => system?.find?.(kind) ?? null)
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* useListener - Register an event listener in Angular component
|
|
31
|
+
*
|
|
32
|
+
* @param {Object} myceliaService - MyceliaService instance
|
|
33
|
+
* @param {string} eventName - Event name/path
|
|
34
|
+
* @param {Function} handler - Handler function
|
|
35
|
+
* @returns {Function} Unsubscribe function
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* @Component({...})
|
|
40
|
+
* export class MyComponent implements OnInit, OnDestroy {
|
|
41
|
+
* private unsubscribe: Function;
|
|
42
|
+
*
|
|
43
|
+
* ngOnInit() {
|
|
44
|
+
* this.unsubscribe = useListener(this.mycelia, 'user:created', (msg) => {
|
|
45
|
+
* console.log('User created:', msg.body);
|
|
46
|
+
* });
|
|
47
|
+
* }
|
|
48
|
+
*
|
|
49
|
+
* ngOnDestroy() {
|
|
50
|
+
* this.unsubscribe();
|
|
51
|
+
* }
|
|
52
|
+
* }
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function useListener(myceliaService, eventName, handler) {
|
|
56
|
+
return myceliaService.useListener(eventName, handler);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* useEventStream - Subscribe to events as an RxJS observable
|
|
61
|
+
*
|
|
62
|
+
* @param {Object} myceliaService - MyceliaService instance
|
|
63
|
+
* @param {string} eventName - Event name/path
|
|
64
|
+
* @param {Object} [options={}] - Options
|
|
65
|
+
* @param {boolean} [options.accumulate=false] - If true, accumulate events
|
|
66
|
+
* @returns {import('rxjs').Observable} Observable that emits event messages
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* @Component({...})
|
|
71
|
+
* export class EventListComponent {
|
|
72
|
+
* events$ = useEventStream(this.mycelia, 'todo:created', { accumulate: true });
|
|
73
|
+
* }
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function useEventStream(myceliaService, eventName, options = {}) {
|
|
77
|
+
const { Subject } = require('rxjs');
|
|
78
|
+
const { scan, startWith } = require('rxjs/operators');
|
|
79
|
+
const { accumulate = false } = options;
|
|
80
|
+
|
|
81
|
+
const subject = new Subject();
|
|
82
|
+
|
|
83
|
+
const unsubscribe = myceliaService.useListener(eventName, (msg) => {
|
|
84
|
+
subject.next(msg.body);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
let stream$ = subject.asObservable();
|
|
88
|
+
|
|
89
|
+
if (accumulate) {
|
|
90
|
+
stream$ = stream$.pipe(
|
|
91
|
+
scan((acc, value) => [...acc, value], []),
|
|
92
|
+
startWith([])
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Note: In Angular, you'd typically handle unsubscription in ngOnDestroy
|
|
97
|
+
// This is a simplified version - real implementation would need proper cleanup
|
|
98
|
+
|
|
99
|
+
return stream$;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|