@plures/praxis 1.1.3 → 1.2.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/FRAMEWORK.md +106 -15
- package/README.md +209 -48
- package/dist/browser/adapter-TM4IS5KT.js +12 -0
- package/dist/browser/chunk-JQ64KMLN.js +141 -0
- package/dist/browser/chunk-LE2ZJYFC.js +154 -0
- package/dist/browser/{chunk-R45WXWKH.js → chunk-VOMLVI6V.js} +1 -149
- package/dist/browser/engine-YJZV4SLD.js +8 -0
- package/dist/browser/index.d.ts +130 -1
- package/dist/browser/index.js +146 -139
- package/dist/browser/integrations/svelte.js +2 -1
- package/dist/node/adapter-K6DOX6XS.js +13 -0
- package/dist/node/chunk-JQ64KMLN.js +141 -0
- package/dist/node/chunk-LE2ZJYFC.js +154 -0
- package/dist/node/chunk-S54337I5.js +446 -0
- package/dist/node/{chunk-R45WXWKH.js → chunk-VOMLVI6V.js} +1 -149
- package/dist/node/cli/index.cjs +1444 -889
- package/dist/node/cli/index.js +9 -0
- package/dist/node/docs-JFNYTOJA.js +102 -0
- package/dist/node/engine-2DQBKBJC.js +9 -0
- package/dist/node/index.cjs +503 -325
- package/dist/node/index.d.cts +130 -1
- package/dist/node/index.d.ts +130 -1
- package/dist/node/index.js +151 -580
- package/dist/node/integrations/svelte.js +2 -1
- package/package.json +1 -1
- package/src/cli/commands/docs.ts +147 -0
- package/src/cli/index.ts +21 -0
- package/src/core/pluresdb/adapter.ts +1 -1
- package/src/core/reactive-engine.svelte.ts +6 -1
- package/src/core/reactive-engine.ts +1 -1
- package/src/index.browser.ts +4 -0
- package/src/index.ts +4 -0
- package/src/integrations/unified.ts +350 -0
package/package.json
CHANGED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Docs Command
|
|
3
|
+
*
|
|
4
|
+
* Generate documentation from Praxis schemas using State-Docs integration.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { createStateDocsGenerator } from '../../integrations/state-docs.js';
|
|
10
|
+
import { loadSchemaFromFile } from '../../core/schema/loader.js';
|
|
11
|
+
import type { PraxisRegistry } from '../../core/rules.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Docs command options
|
|
15
|
+
*/
|
|
16
|
+
export interface DocsOptions {
|
|
17
|
+
/** Output directory for generated docs */
|
|
18
|
+
output?: string;
|
|
19
|
+
/** Documentation title */
|
|
20
|
+
title?: string;
|
|
21
|
+
/** Include table of contents */
|
|
22
|
+
toc?: boolean;
|
|
23
|
+
/** Include timestamp */
|
|
24
|
+
timestamp?: boolean;
|
|
25
|
+
/** Visualization format */
|
|
26
|
+
format?: 'mermaid' | 'dot';
|
|
27
|
+
/** Custom header content */
|
|
28
|
+
header?: string;
|
|
29
|
+
/** Custom footer content */
|
|
30
|
+
footer?: string;
|
|
31
|
+
/** Generate from registry instead of schema */
|
|
32
|
+
fromRegistry?: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Generate documentation from schema or registry
|
|
37
|
+
*/
|
|
38
|
+
export async function docs(
|
|
39
|
+
schemaOrRegistryPath: string | undefined,
|
|
40
|
+
options: DocsOptions
|
|
41
|
+
): Promise<void> {
|
|
42
|
+
console.log('\n╔═══════════════════════════════════════════════════╗');
|
|
43
|
+
console.log('║ Praxis Documentation Generator ║');
|
|
44
|
+
console.log('╚═══════════════════════════════════════════════════╝\n');
|
|
45
|
+
|
|
46
|
+
if (!schemaOrRegistryPath || !fs.existsSync(schemaOrRegistryPath)) {
|
|
47
|
+
console.error('Error: Schema or registry file required');
|
|
48
|
+
console.log('Usage: praxis docs <schema-file> [options]');
|
|
49
|
+
console.log('\nOptions:');
|
|
50
|
+
console.log(' --output <dir> Output directory (default: ./docs)');
|
|
51
|
+
console.log(' --title <title> Documentation title');
|
|
52
|
+
console.log(' --format <format> Diagram format: mermaid (default) or dot');
|
|
53
|
+
console.log(' --no-toc Disable table of contents');
|
|
54
|
+
console.log(' --no-timestamp Disable timestamp');
|
|
55
|
+
console.log(' --from-registry Generate from registry instead of schema');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const outputDir = options.output || './docs';
|
|
60
|
+
const title = options.title || 'Praxis Application';
|
|
61
|
+
|
|
62
|
+
// Create generator
|
|
63
|
+
const generator = createStateDocsGenerator({
|
|
64
|
+
projectTitle: title,
|
|
65
|
+
target: outputDir,
|
|
66
|
+
visualization: {
|
|
67
|
+
format: options.format || 'mermaid',
|
|
68
|
+
exportPng: false,
|
|
69
|
+
},
|
|
70
|
+
template: {
|
|
71
|
+
toc: options.toc !== false,
|
|
72
|
+
timestamp: options.timestamp !== false,
|
|
73
|
+
header: options.header,
|
|
74
|
+
footer: options.footer,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
console.log(`Source: ${schemaOrRegistryPath}`);
|
|
79
|
+
console.log(`Output: ${outputDir}\n`);
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
let generatedDocs;
|
|
83
|
+
|
|
84
|
+
if (options.fromRegistry) {
|
|
85
|
+
// Load registry module
|
|
86
|
+
console.log('Loading registry module...');
|
|
87
|
+
const module = await import(path.resolve(schemaOrRegistryPath));
|
|
88
|
+
const registry: PraxisRegistry<unknown> =
|
|
89
|
+
module.registry || module.default || module;
|
|
90
|
+
|
|
91
|
+
if (!registry || typeof registry.getAllRules !== 'function') {
|
|
92
|
+
console.error('Error: Invalid registry module');
|
|
93
|
+
console.log('Expected: export const registry = new PraxisRegistry()');
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log('Generating documentation from registry...');
|
|
98
|
+
// Create module object from registry
|
|
99
|
+
const praxisModule = {
|
|
100
|
+
rules: registry.getAllRules(),
|
|
101
|
+
constraints: registry.getAllConstraints(),
|
|
102
|
+
};
|
|
103
|
+
generatedDocs = generator.generateFromModule(praxisModule);
|
|
104
|
+
} else {
|
|
105
|
+
// Load schema
|
|
106
|
+
console.log('Loading schema...');
|
|
107
|
+
const result = await loadSchemaFromFile(schemaOrRegistryPath);
|
|
108
|
+
|
|
109
|
+
if (result.errors.length > 0 || !result.schema) {
|
|
110
|
+
console.error(`Error loading schema: ${result.errors.join(', ')}`);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
console.log('Generating documentation from schema...');
|
|
115
|
+
generatedDocs = generator.generateFromSchema(result.schema);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Ensure output directory exists
|
|
119
|
+
if (!fs.existsSync(outputDir)) {
|
|
120
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Write all generated docs
|
|
124
|
+
console.log('\nWriting documentation files:\n');
|
|
125
|
+
for (const doc of generatedDocs) {
|
|
126
|
+
const fullPath = path.resolve(doc.path);
|
|
127
|
+
const dir = path.dirname(fullPath);
|
|
128
|
+
|
|
129
|
+
// Ensure directory exists
|
|
130
|
+
if (!fs.existsSync(dir)) {
|
|
131
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
fs.writeFileSync(fullPath, doc.content);
|
|
135
|
+
console.log(` ✓ ${doc.path} (${doc.type})`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
console.log(`\n✓ Generated ${generatedDocs.length} documentation file(s)`);
|
|
139
|
+
console.log(`\nView your documentation: ${path.resolve(outputDir, 'README.md')}`);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(`Error generating documentation: ${error}`);
|
|
142
|
+
if (error instanceof Error) {
|
|
143
|
+
console.error(error.stack);
|
|
144
|
+
}
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -90,6 +90,27 @@ program
|
|
|
90
90
|
await generate(options);
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
+
program
|
|
94
|
+
.command('docs [schema]')
|
|
95
|
+
.description('Generate documentation from schemas or registries')
|
|
96
|
+
.option('-o, --output <dir>', 'Output directory', './docs')
|
|
97
|
+
.option('--title <title>', 'Documentation title')
|
|
98
|
+
.option('--format <format>', 'Diagram format (mermaid, dot)', 'mermaid')
|
|
99
|
+
.option('--no-toc', 'Disable table of contents')
|
|
100
|
+
.option('--no-timestamp', 'Disable timestamp')
|
|
101
|
+
.option('--from-registry', 'Generate from registry instead of schema')
|
|
102
|
+
.option('--header <content>', 'Custom header content')
|
|
103
|
+
.option('--footer <content>', 'Custom footer content')
|
|
104
|
+
.action(async (schema, options) => {
|
|
105
|
+
try {
|
|
106
|
+
const { docs } = await import('./commands/docs.js');
|
|
107
|
+
await docs(schema, options);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error('Error generating documentation:', error);
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
93
114
|
program
|
|
94
115
|
.command('canvas [schema]')
|
|
95
116
|
.description('Open CodeCanvas for visual editing')
|
|
@@ -143,7 +143,7 @@ export interface PluresDBAdapterConfig {
|
|
|
143
143
|
export class PluresDBPraxisAdapter implements PraxisDB {
|
|
144
144
|
private db: PluresDBInstance;
|
|
145
145
|
private watchers = new Map<string, Set<(val: unknown) => void>>();
|
|
146
|
-
private pollIntervals = new Map<string,
|
|
146
|
+
private pollIntervals = new Map<string, ReturnType<typeof setInterval>>();
|
|
147
147
|
private lastValues = new Map<string, unknown>();
|
|
148
148
|
private pollInterval: number;
|
|
149
149
|
|
|
@@ -9,6 +9,11 @@ import { PraxisRegistry } from '../core/rules.js';
|
|
|
9
9
|
import type { PraxisEvent } from '../core/protocol.js';
|
|
10
10
|
import { LogicEngine, createPraxisEngine } from '../core/engine.js';
|
|
11
11
|
|
|
12
|
+
// Type declaration for Svelte 5 $state rune
|
|
13
|
+
// This is needed for TypeScript compilation; the actual implementation
|
|
14
|
+
// is provided by the Svelte compiler when processing .svelte.ts files
|
|
15
|
+
declare function $state<T>(initial: T): T;
|
|
16
|
+
|
|
12
17
|
export interface ReactiveEngineOptions<TContext> {
|
|
13
18
|
initialContext: TContext;
|
|
14
19
|
initialFacts?: any[];
|
|
@@ -22,7 +27,7 @@ export interface ReactiveEngineOptions<TContext> {
|
|
|
22
27
|
*/
|
|
23
28
|
export class ReactiveLogicEngine<TContext extends object> {
|
|
24
29
|
// Use Svelte's $state rune for automatic reactivity
|
|
25
|
-
state = $state({
|
|
30
|
+
state: { context: TContext; facts: any[]; meta: Record<string, unknown> } = $state({
|
|
26
31
|
context: {} as TContext,
|
|
27
32
|
facts: [] as any[],
|
|
28
33
|
meta: {} as Record<string, unknown>
|
|
@@ -151,7 +151,7 @@ export class ReactiveLogicEngine<TContext extends object> {
|
|
|
151
151
|
/**
|
|
152
152
|
* Get the full state object
|
|
153
153
|
*/
|
|
154
|
-
get state() {
|
|
154
|
+
get state(): { context: TContext; facts: any[]; meta: Record<string, unknown> } {
|
|
155
155
|
return {
|
|
156
156
|
context: this._contextProxy,
|
|
157
157
|
facts: this._factsProxy,
|
package/src/index.browser.ts
CHANGED
|
@@ -214,3 +214,7 @@ export {
|
|
|
214
214
|
attachTauriToEngine,
|
|
215
215
|
generateTauriConfig,
|
|
216
216
|
} from './integrations/tauri.js';
|
|
217
|
+
|
|
218
|
+
// Unified Integration Helpers
|
|
219
|
+
export type { UnifiedAppConfig, UnifiedApp } from './integrations/unified.js';
|
|
220
|
+
export { createUnifiedApp, attachAllIntegrations } from './integrations/unified.js';
|
package/src/index.ts
CHANGED
|
@@ -268,3 +268,7 @@ export {
|
|
|
268
268
|
attachTauriToEngine,
|
|
269
269
|
generateTauriConfig,
|
|
270
270
|
} from './integrations/tauri.js';
|
|
271
|
+
|
|
272
|
+
// Unified Integration Helpers
|
|
273
|
+
export type { UnifiedAppConfig, UnifiedApp } from './integrations/unified.js';
|
|
274
|
+
export { createUnifiedApp, attachAllIntegrations } from './integrations/unified.js';
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Integration Helpers
|
|
3
|
+
*
|
|
4
|
+
* Convenience functions for setting up Praxis with all ecosystem integrations
|
|
5
|
+
* (PluresDB, Unum, State-Docs, CodeCanvas) in a single call.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { LogicEngine } from '../core/engine.js';
|
|
9
|
+
import type { PraxisRegistry, PraxisModule } from '../core/rules.js';
|
|
10
|
+
import type { PraxisSchema } from '../core/schema/types.js';
|
|
11
|
+
import type { PraxisDB, UnsubscribeFn } from '../core/pluresdb/adapter.js';
|
|
12
|
+
import type { UnumIdentity } from './unum.js';
|
|
13
|
+
import {
|
|
14
|
+
createPluresDBAdapter,
|
|
15
|
+
generateId,
|
|
16
|
+
type PluresDBAdapter,
|
|
17
|
+
} from './pluresdb.js';
|
|
18
|
+
import {
|
|
19
|
+
createUnumAdapter,
|
|
20
|
+
attachUnumToEngine,
|
|
21
|
+
type UnumAdapter,
|
|
22
|
+
type UnumChannel,
|
|
23
|
+
} from './unum.js';
|
|
24
|
+
import {
|
|
25
|
+
createStateDocsGenerator,
|
|
26
|
+
type StateDocsGenerator,
|
|
27
|
+
type GeneratedDoc,
|
|
28
|
+
} from './state-docs.js';
|
|
29
|
+
import {
|
|
30
|
+
schemaToCanvas,
|
|
31
|
+
type CanvasDocument,
|
|
32
|
+
} from './code-canvas.js';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Configuration for unified Praxis application
|
|
36
|
+
*/
|
|
37
|
+
export interface UnifiedAppConfig<TContext = unknown> {
|
|
38
|
+
/** Praxis registry with rules and constraints */
|
|
39
|
+
registry: PraxisRegistry<TContext>;
|
|
40
|
+
|
|
41
|
+
/** Initial context for the engine */
|
|
42
|
+
initialContext: TContext;
|
|
43
|
+
|
|
44
|
+
/** PluresDB instance (if not provided, creates in-memory DB) */
|
|
45
|
+
db?: PraxisDB;
|
|
46
|
+
|
|
47
|
+
/** Enable Unum for distributed communication */
|
|
48
|
+
enableUnum?: boolean;
|
|
49
|
+
|
|
50
|
+
/** Unum identity configuration (without id and createdAt which are auto-generated) */
|
|
51
|
+
unumIdentity?: Omit<UnumIdentity, 'id' | 'createdAt'>;
|
|
52
|
+
|
|
53
|
+
/** Enable State-Docs documentation generation */
|
|
54
|
+
enableDocs?: boolean;
|
|
55
|
+
|
|
56
|
+
/** State-Docs configuration */
|
|
57
|
+
docsConfig?: {
|
|
58
|
+
projectTitle: string;
|
|
59
|
+
target?: string;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** Praxis schema for CodeCanvas integration */
|
|
63
|
+
schema?: PraxisSchema;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Unified application instance with all integrations
|
|
68
|
+
*/
|
|
69
|
+
export interface UnifiedApp<TContext = unknown> {
|
|
70
|
+
/** Praxis logic engine */
|
|
71
|
+
engine: LogicEngine<TContext>;
|
|
72
|
+
|
|
73
|
+
/** PluresDB adapter for persistence */
|
|
74
|
+
pluresdb: PluresDBAdapter<TContext>;
|
|
75
|
+
|
|
76
|
+
/** Unum adapter for distributed communication (if enabled) */
|
|
77
|
+
unum?: UnumAdapter;
|
|
78
|
+
|
|
79
|
+
/** Default Unum channel (if Unum enabled) */
|
|
80
|
+
channel?: UnumChannel;
|
|
81
|
+
|
|
82
|
+
/** State-Docs generator (if enabled) */
|
|
83
|
+
docs?: StateDocsGenerator;
|
|
84
|
+
|
|
85
|
+
/** CodeCanvas document (if schema provided) */
|
|
86
|
+
canvas?: CanvasDocument;
|
|
87
|
+
|
|
88
|
+
/** Generate documentation from current state */
|
|
89
|
+
generateDocs?: () => GeneratedDoc[];
|
|
90
|
+
|
|
91
|
+
/** Cleanup function to dispose all integrations */
|
|
92
|
+
dispose: () => void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create a unified Praxis application with all integrations
|
|
97
|
+
*
|
|
98
|
+
* This is a convenience function that sets up:
|
|
99
|
+
* - Praxis logic engine
|
|
100
|
+
* - PluresDB for persistence (auto-attaches to engine)
|
|
101
|
+
* - Unum for distributed communication (optional)
|
|
102
|
+
* - State-Docs for documentation generation (optional)
|
|
103
|
+
* - CodeCanvas for visual schema editing (optional)
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* import { createUnifiedApp } from '@plures/praxis';
|
|
108
|
+
*
|
|
109
|
+
* const app = await createUnifiedApp({
|
|
110
|
+
* registry: myRegistry,
|
|
111
|
+
* initialContext: { count: 0 },
|
|
112
|
+
* enableUnum: true,
|
|
113
|
+
* unumIdentity: { name: 'node-1' },
|
|
114
|
+
* enableDocs: true,
|
|
115
|
+
* docsConfig: { projectTitle: 'My App' },
|
|
116
|
+
* schema: mySchema,
|
|
117
|
+
* });
|
|
118
|
+
*
|
|
119
|
+
* // Use the engine
|
|
120
|
+
* app.engine.step([myEvent]);
|
|
121
|
+
*
|
|
122
|
+
* // Broadcast to other nodes
|
|
123
|
+
* if (app.channel) {
|
|
124
|
+
* await app.unum?.broadcastEvent(app.channel.id, myEvent);
|
|
125
|
+
* }
|
|
126
|
+
*
|
|
127
|
+
* // Generate documentation
|
|
128
|
+
* const docs = app.generateDocs?.();
|
|
129
|
+
*
|
|
130
|
+
* // Cleanup
|
|
131
|
+
* app.dispose();
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export async function createUnifiedApp<TContext = unknown>(
|
|
135
|
+
config: UnifiedAppConfig<TContext>
|
|
136
|
+
): Promise<UnifiedApp<TContext>> {
|
|
137
|
+
const { createPraxisEngine } = await import('../core/engine.js');
|
|
138
|
+
const { createInMemoryDB } = await import('../core/pluresdb/adapter.js');
|
|
139
|
+
|
|
140
|
+
// Create database if not provided
|
|
141
|
+
const db = config.db || createInMemoryDB();
|
|
142
|
+
|
|
143
|
+
// Create PluresDB adapter
|
|
144
|
+
const pluresdb = createPluresDBAdapter({
|
|
145
|
+
db,
|
|
146
|
+
registry: config.registry,
|
|
147
|
+
initialContext: config.initialContext,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Create Praxis engine
|
|
151
|
+
const engine = createPraxisEngine({
|
|
152
|
+
initialContext: config.initialContext,
|
|
153
|
+
registry: config.registry,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Attach PluresDB to engine
|
|
157
|
+
pluresdb.attachEngine(engine);
|
|
158
|
+
|
|
159
|
+
const disposers: UnsubscribeFn[] = [];
|
|
160
|
+
|
|
161
|
+
// Setup Unum if enabled
|
|
162
|
+
let unum: UnumAdapter | undefined;
|
|
163
|
+
let channel: UnumChannel | undefined;
|
|
164
|
+
if (config.enableUnum) {
|
|
165
|
+
// Convert partial identity to full identity if provided
|
|
166
|
+
const fullIdentity: UnumIdentity | undefined = config.unumIdentity
|
|
167
|
+
? {
|
|
168
|
+
...config.unumIdentity,
|
|
169
|
+
id: generateId(),
|
|
170
|
+
createdAt: Date.now(),
|
|
171
|
+
}
|
|
172
|
+
: undefined;
|
|
173
|
+
|
|
174
|
+
unum = await createUnumAdapter({
|
|
175
|
+
db,
|
|
176
|
+
identity: fullIdentity,
|
|
177
|
+
realtime: true,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Create default channel
|
|
181
|
+
channel = await unum.createChannel(
|
|
182
|
+
config.unumIdentity?.name || 'praxis-app',
|
|
183
|
+
[]
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Attach Unum to engine
|
|
187
|
+
const unumDisposer = attachUnumToEngine(engine, unum, channel.id);
|
|
188
|
+
disposers.push(unumDisposer);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Setup State-Docs if enabled
|
|
192
|
+
let docs: StateDocsGenerator | undefined;
|
|
193
|
+
let generateDocs: (() => GeneratedDoc[]) | undefined;
|
|
194
|
+
if (config.enableDocs && config.docsConfig) {
|
|
195
|
+
docs = createStateDocsGenerator({
|
|
196
|
+
projectTitle: config.docsConfig.projectTitle,
|
|
197
|
+
target: config.docsConfig.target || './docs',
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
generateDocs = () => {
|
|
201
|
+
// Get rules and constraints from registry
|
|
202
|
+
const module: PraxisModule<TContext> = {
|
|
203
|
+
rules: config.registry.getAllRules(),
|
|
204
|
+
constraints: config.registry.getAllConstraints(),
|
|
205
|
+
};
|
|
206
|
+
return docs!.generateFromModule(module);
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Setup CodeCanvas if schema provided
|
|
211
|
+
let canvas: CanvasDocument | undefined;
|
|
212
|
+
if (config.schema) {
|
|
213
|
+
// Convert PraxisSchema to PSFSchema format expected by schemaToCanvas
|
|
214
|
+
// Both types are structurally compatible, so we can safely cast
|
|
215
|
+
canvas = schemaToCanvas(config.schema as unknown as import('../../core/schema-engine/psf.js').PSFSchema, { layout: 'hierarchical' });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
engine,
|
|
220
|
+
pluresdb,
|
|
221
|
+
unum,
|
|
222
|
+
channel,
|
|
223
|
+
docs,
|
|
224
|
+
canvas,
|
|
225
|
+
generateDocs,
|
|
226
|
+
dispose: () => {
|
|
227
|
+
pluresdb.dispose();
|
|
228
|
+
if (unum) {
|
|
229
|
+
// Disconnect from Unum, log errors during cleanup
|
|
230
|
+
unum.disconnect().catch((err) => {
|
|
231
|
+
console.warn('Warning: Error during Unum disconnect:', err);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
for (const disposer of disposers) {
|
|
235
|
+
disposer();
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Attach all available integrations to an existing Praxis engine
|
|
243
|
+
*
|
|
244
|
+
* This is useful when you already have an engine and want to add integrations.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```typescript
|
|
248
|
+
* import { createPraxisEngine, attachAllIntegrations } from '@plures/praxis';
|
|
249
|
+
*
|
|
250
|
+
* const engine = createPraxisEngine({ initialContext: {}, registry });
|
|
251
|
+
*
|
|
252
|
+
* const integrations = await attachAllIntegrations(engine, registry, {
|
|
253
|
+
* enableUnum: true,
|
|
254
|
+
* enableDocs: true,
|
|
255
|
+
* });
|
|
256
|
+
*
|
|
257
|
+
* // Later cleanup
|
|
258
|
+
* integrations.dispose();
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
export async function attachAllIntegrations<TContext = unknown>(
|
|
262
|
+
engine: LogicEngine<TContext>,
|
|
263
|
+
registry: PraxisRegistry<TContext>,
|
|
264
|
+
options: {
|
|
265
|
+
db?: PraxisDB;
|
|
266
|
+
enableUnum?: boolean;
|
|
267
|
+
unumIdentity?: Omit<UnumIdentity, 'id' | 'createdAt'>;
|
|
268
|
+
enableDocs?: boolean;
|
|
269
|
+
docsConfig?: { projectTitle: string; target?: string };
|
|
270
|
+
} = {}
|
|
271
|
+
): Promise<{
|
|
272
|
+
pluresdb: PluresDBAdapter<TContext>;
|
|
273
|
+
unum?: UnumAdapter;
|
|
274
|
+
channel?: UnumChannel;
|
|
275
|
+
docs?: StateDocsGenerator;
|
|
276
|
+
dispose: () => void;
|
|
277
|
+
}> {
|
|
278
|
+
const { createInMemoryDB } = await import('../core/pluresdb/adapter.js');
|
|
279
|
+
|
|
280
|
+
// Create database if not provided
|
|
281
|
+
const db = options.db || createInMemoryDB();
|
|
282
|
+
|
|
283
|
+
// Create PluresDB adapter
|
|
284
|
+
const pluresdb = createPluresDBAdapter({
|
|
285
|
+
db,
|
|
286
|
+
registry,
|
|
287
|
+
initialContext: engine.getContext(),
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Attach PluresDB to engine
|
|
291
|
+
pluresdb.attachEngine(engine);
|
|
292
|
+
|
|
293
|
+
const disposers: UnsubscribeFn[] = [];
|
|
294
|
+
|
|
295
|
+
// Setup Unum if enabled
|
|
296
|
+
let unum: UnumAdapter | undefined;
|
|
297
|
+
let channel: UnumChannel | undefined;
|
|
298
|
+
if (options.enableUnum) {
|
|
299
|
+
// Convert partial identity to full identity if provided
|
|
300
|
+
const fullIdentity: UnumIdentity | undefined = options.unumIdentity
|
|
301
|
+
? {
|
|
302
|
+
...options.unumIdentity,
|
|
303
|
+
id: generateId(),
|
|
304
|
+
createdAt: Date.now(),
|
|
305
|
+
}
|
|
306
|
+
: undefined;
|
|
307
|
+
|
|
308
|
+
unum = await createUnumAdapter({
|
|
309
|
+
db,
|
|
310
|
+
identity: fullIdentity,
|
|
311
|
+
realtime: true,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
channel = await unum.createChannel(
|
|
315
|
+
options.unumIdentity?.name || 'praxis-app',
|
|
316
|
+
[]
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
const unumDisposer = attachUnumToEngine(engine, unum, channel.id);
|
|
320
|
+
disposers.push(unumDisposer);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Setup State-Docs if enabled
|
|
324
|
+
let docs: StateDocsGenerator | undefined;
|
|
325
|
+
if (options.enableDocs && options.docsConfig) {
|
|
326
|
+
docs = createStateDocsGenerator({
|
|
327
|
+
projectTitle: options.docsConfig.projectTitle,
|
|
328
|
+
target: options.docsConfig.target || './docs',
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
pluresdb,
|
|
334
|
+
unum,
|
|
335
|
+
channel,
|
|
336
|
+
docs,
|
|
337
|
+
dispose: () => {
|
|
338
|
+
pluresdb.dispose();
|
|
339
|
+
if (unum) {
|
|
340
|
+
// Disconnect from Unum, log errors during cleanup
|
|
341
|
+
unum.disconnect().catch((err) => {
|
|
342
|
+
console.warn('Warning: Error during Unum disconnect:', err);
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
for (const disposer of disposers) {
|
|
346
|
+
disposer();
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
}
|