@rangka/core 0.1.0 → 0.1.2
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/package.json +6 -2
- package/.claude/skills/extend-core/SKILL.md +0 -133
- package/.turbo/turbo-build.log +0 -4
- package/CHANGELOG.md +0 -18
- package/CLAUDE.md +0 -180
- package/src/__tests__/coerce.test.ts +0 -154
- package/src/__tests__/context.test.ts +0 -111
- package/src/__tests__/helpers.ts +0 -21
- package/src/__tests__/index.test.ts +0 -7
- package/src/__tests__/widgets.test.ts +0 -197
- package/src/api/__tests__/handlers.test.ts +0 -389
- package/src/api/__tests__/include-resolver.test.ts +0 -393
- package/src/api/__tests__/middleware.test.ts +0 -100
- package/src/api/__tests__/openapi-schema.test.ts +0 -210
- package/src/api/__tests__/query-parser.test.ts +0 -291
- package/src/api/__tests__/route-generator.test.ts +0 -137
- package/src/api/__tests__/server.test.ts +0 -73
- package/src/api/__tests__/swagger.test.ts +0 -166
- package/src/api/handlers.ts +0 -274
- package/src/api/include-resolver.ts +0 -27
- package/src/api/index.ts +0 -4
- package/src/api/meta-handler.ts +0 -254
- package/src/api/openapi-schema.ts +0 -99
- package/src/api/query-parser.ts +0 -315
- package/src/api/route-generator.ts +0 -448
- package/src/api/server.ts +0 -147
- package/src/api/types.ts +0 -16
- package/src/audit/__tests__/audit.test.ts +0 -144
- package/src/audit/index.ts +0 -3
- package/src/audit/record.ts +0 -69
- package/src/audit/tables.ts +0 -48
- package/src/audit/types.ts +0 -26
- package/src/auth/__tests__/core-module.test.ts +0 -54
- package/src/auth/__tests__/debug.test.ts +0 -47
- package/src/auth/__tests__/field-permissions.test.ts +0 -245
- package/src/auth/__tests__/integration.test.ts +0 -208
- package/src/auth/__tests__/meta-boot.test.ts +0 -538
- package/src/auth/__tests__/model-permissions.test.ts +0 -205
- package/src/auth/__tests__/password.test.ts +0 -29
- package/src/auth/__tests__/permission-registry.test.ts +0 -313
- package/src/auth/__tests__/scope-hook.test.ts +0 -509
- package/src/auth/__tests__/scope-registry.test.ts +0 -297
- package/src/auth/__tests__/scopes.test.ts +0 -66
- package/src/auth/__tests__/session.test.ts +0 -214
- package/src/auth/core-models.ts +0 -52
- package/src/auth/core-module.ts +0 -59
- package/src/auth/debug.ts +0 -157
- package/src/auth/field-permissions.ts +0 -116
- package/src/auth/index.ts +0 -37
- package/src/auth/model-permissions.ts +0 -59
- package/src/auth/password.ts +0 -22
- package/src/auth/permission-registry.ts +0 -171
- package/src/auth/scope-filters.ts +0 -11
- package/src/auth/scope-registry.ts +0 -121
- package/src/auth/scopes.ts +0 -146
- package/src/auth/seed.ts +0 -44
- package/src/auth/session.ts +0 -178
- package/src/auth/types.ts +0 -50
- package/src/boot/__tests__/page-scanning.test.ts +0 -170
- package/src/boot/__tests__/page-utils.test.ts +0 -225
- package/src/boot/__tests__/project-scanner.test.ts +0 -88
- package/src/boot/dependency-sort.ts +0 -82
- package/src/boot/discovery.ts +0 -85
- package/src/boot/index.ts +0 -457
- package/src/boot/page-utils.ts +0 -110
- package/src/boot/project-scanner.ts +0 -397
- package/src/boot/schema-loader.ts +0 -26
- package/src/boot/schema-merger.ts +0 -125
- package/src/boot/traits.ts +0 -25
- package/src/boot/types.ts +0 -73
- package/src/context.ts +0 -105
- package/src/db/__tests__/cascade-delete.test.ts +0 -182
- package/src/db/__tests__/desired-state.test.ts +0 -136
- package/src/db/__tests__/diff-engine.test.ts +0 -635
- package/src/db/__tests__/field-mapper.test.ts +0 -355
- package/src/db/__tests__/introspect.test.ts +0 -70
- package/src/db/__tests__/search-filter.test.ts +0 -45
- package/src/db/__tests__/sequence.test.ts +0 -221
- package/src/db/auto-sync.ts +0 -133
- package/src/db/client.ts +0 -147
- package/src/db/desired-state.ts +0 -98
- package/src/db/diff-engine.ts +0 -305
- package/src/db/field-mapper.ts +0 -504
- package/src/db/filter-applier.ts +0 -89
- package/src/db/include-resolver.ts +0 -40
- package/src/db/index.ts +0 -23
- package/src/db/introspect.ts +0 -265
- package/src/db/model-include-resolver.ts +0 -327
- package/src/db/model-ops.ts +0 -281
- package/src/db/scope-enforcer.ts +0 -37
- package/src/db/types.ts +0 -98
- package/src/errors.ts +0 -41
- package/src/events/__tests__/bus.test.ts +0 -105
- package/src/events/bus.ts +0 -89
- package/src/events/index.ts +0 -2
- package/src/events/types.ts +0 -9
- package/src/external-model/__tests__/computed-fields.test.ts +0 -106
- package/src/external-model/__tests__/field-mapper.test.ts +0 -160
- package/src/external-model/__tests__/in-memory-ops.test.ts +0 -247
- package/src/external-model/__tests__/mutation-executor.test.ts +0 -160
- package/src/external-model/__tests__/query-executor.test.ts +0 -284
- package/src/external-model/__tests__/schema-converter.test.ts +0 -174
- package/src/external-model/computed-fields.ts +0 -15
- package/src/external-model/define.ts +0 -5
- package/src/external-model/external-model-ops.ts +0 -108
- package/src/external-model/field-mapper.ts +0 -66
- package/src/external-model/in-memory-ops.ts +0 -107
- package/src/external-model/index.ts +0 -7
- package/src/external-model/mutation-executor.ts +0 -71
- package/src/external-model/query-executor.ts +0 -100
- package/src/external-model/schema-converter.ts +0 -53
- package/src/external-model/types.ts +0 -32
- package/src/fixtures/__tests__/fixtures.test.ts +0 -203
- package/src/fixtures/index.ts +0 -10
- package/src/fixtures/loader.ts +0 -196
- package/src/fixtures/registry.ts +0 -125
- package/src/fixtures/types.ts +0 -33
- package/src/helpers/assert-ownership.ts +0 -19
- package/src/helpers/coerce.ts +0 -28
- package/src/helpers/stamping.ts +0 -28
- package/src/helpers/validation.ts +0 -14
- package/src/hooks/__tests__/context.test.ts +0 -73
- package/src/hooks/__tests__/executor.test.ts +0 -433
- package/src/hooks/__tests__/middleware.test.ts +0 -224
- package/src/hooks/__tests__/registry.test.ts +0 -50
- package/src/hooks/context.ts +0 -89
- package/src/hooks/errors.ts +0 -11
- package/src/hooks/executor.ts +0 -115
- package/src/hooks/index.ts +0 -10
- package/src/hooks/middleware.ts +0 -220
- package/src/hooks/registry.ts +0 -20
- package/src/hooks/types.ts +0 -32
- package/src/index.ts +0 -172
- package/src/jobs/__tests__/enqueue.test.ts +0 -77
- package/src/jobs/__tests__/integration.test.ts +0 -71
- package/src/jobs/__tests__/registry.test.ts +0 -103
- package/src/jobs/__tests__/scheduler.test.ts +0 -92
- package/src/jobs/__tests__/worker-execution.test.ts +0 -202
- package/src/jobs/__tests__/worker.test.ts +0 -119
- package/src/jobs/enqueue.ts +0 -93
- package/src/jobs/index.ts +0 -14
- package/src/jobs/registry.ts +0 -92
- package/src/jobs/scheduler.ts +0 -205
- package/src/jobs/tables.ts +0 -132
- package/src/jobs/types.ts +0 -62
- package/src/jobs/worker.ts +0 -272
- package/src/model-api/__tests__/cross-boundary-includes.test.ts +0 -366
- package/src/model-api/__tests__/extended-api.test.ts +0 -244
- package/src/model-api/__tests__/filter-applier.test.ts +0 -177
- package/src/model-api/__tests__/filter-translator.test.ts +0 -186
- package/src/model-api/__tests__/include-resolver.test.ts +0 -226
- package/src/model-api/__tests__/model-access.test.ts +0 -284
- package/src/model-api/__tests__/query-builder.test.ts +0 -224
- package/src/model-api/__tests__/scope-enforcer.test.ts +0 -268
- package/src/model-api/field-access.ts +0 -28
- package/src/model-api/filter-applier.ts +0 -1
- package/src/model-api/filter-translator.ts +0 -67
- package/src/model-api/include-resolver.ts +0 -2
- package/src/model-api/index.ts +0 -86
- package/src/model-api/query-builder.ts +0 -155
- package/src/model-api/scope-enforcer.ts +0 -3
- package/src/model-api/types.ts +0 -139
- package/src/plugins/__tests__/adapter-registry.test.ts +0 -92
- package/src/plugins/__tests__/lifecycle.test.ts +0 -96
- package/src/plugins/__tests__/loader.test.ts +0 -273
- package/src/plugins/__tests__/validator.test.ts +0 -275
- package/src/plugins/adapter-registry.ts +0 -42
- package/src/plugins/define.ts +0 -5
- package/src/plugins/index.ts +0 -28
- package/src/plugins/lifecycle.ts +0 -27
- package/src/plugins/loader.ts +0 -126
- package/src/plugins/types.ts +0 -76
- package/src/plugins/validator.ts +0 -141
- package/src/schema/__tests__/registry-models-by-module.test.ts +0 -58
- package/src/schema/registry.ts +0 -93
- package/src/schema/relationships.ts +0 -93
- package/src/schema/types.ts +0 -43
- package/src/services/__tests__/integration.test.ts +0 -63
- package/src/services/__tests__/registry.test.ts +0 -175
- package/src/services/index.ts +0 -13
- package/src/services/registry.ts +0 -156
- package/src/services/types.ts +0 -27
- package/src/validation/__tests__/field-validator.test.ts +0 -195
- package/src/validation/field-validator.ts +0 -113
- package/src/validation/index.ts +0 -1
- package/src/widgets/index.ts +0 -3
- package/src/widgets/slot-validator.ts +0 -87
- package/src/widgets/widget-registry.ts +0 -32
- package/tests/boot.test.ts +0 -323
- package/tests/dependency-sort.test.ts +0 -99
- package/tests/discovery.test.ts +0 -126
- package/tests/registry.test.ts +0 -216
- package/tests/schema-loader.test.ts +0 -52
- package/tests/schema-merger.test.ts +0 -180
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -14
package/src/boot/index.ts
DELETED
|
@@ -1,457 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import type { FrameworkContext } from '@rangka/shared';
|
|
3
|
-
import type { FastifyInstance } from 'fastify';
|
|
4
|
-
import type { DiscoverySource, DiscoveredApp } from './types.js';
|
|
5
|
-
import { dependencySort } from './dependency-sort.js';
|
|
6
|
-
import { loadSchemas } from './schema-loader.js';
|
|
7
|
-
import { mergeSchemas } from './schema-merger.js';
|
|
8
|
-
import { SchemaRegistry } from '../schema/registry.js';
|
|
9
|
-
import { DatabaseClient } from '../db/client.js';
|
|
10
|
-
import type { DatabaseClientConfig } from '../db/client.js';
|
|
11
|
-
import { autoSync } from '../db/auto-sync.js';
|
|
12
|
-
import { createServer } from '../api/server.js';
|
|
13
|
-
import { generateRoutes } from '../api/route-generator.js';
|
|
14
|
-
import type { ServerConfig } from '../api/types.js';
|
|
15
|
-
import { PermissionRegistry } from '../auth/permission-registry.js';
|
|
16
|
-
import { HookRegistry } from '../hooks/registry.js';
|
|
17
|
-
import { JobRegistry } from '../jobs/registry.js';
|
|
18
|
-
import { JobWorker } from '../jobs/worker.js';
|
|
19
|
-
import { ScheduleManager } from '../jobs/scheduler.js';
|
|
20
|
-
import { EventBus } from '../events/bus.js';
|
|
21
|
-
import { ServiceRegistry } from '../services/registry.js';
|
|
22
|
-
import { FixtureRegistry } from '../fixtures/registry.js';
|
|
23
|
-
import { createFrameworkContext } from '../context.js';
|
|
24
|
-
import { getCoreApp, getCoreModels } from '../auth/core-module.js';
|
|
25
|
-
import { seedCoreData } from '../auth/seed.js';
|
|
26
|
-
import { ScopeRegistry } from '../auth/scope-registry.js';
|
|
27
|
-
import { WidgetRegistry } from '../widgets/widget-registry.js';
|
|
28
|
-
import { AdapterRegistry } from '../plugins/adapter-registry.js';
|
|
29
|
-
import { PluginLifecycleManager } from '../plugins/lifecycle.js';
|
|
30
|
-
import { loadPlugins } from '../plugins/loader.js';
|
|
31
|
-
import type { PluginDefinition } from '../plugins/types.js';
|
|
32
|
-
import type { RolesConfig, JobConfig } from '@rangka/shared';
|
|
33
|
-
import type { JobWorkerConfig } from '../jobs/types.js';
|
|
34
|
-
import type { ServiceDefinition } from '../services/types.js';
|
|
35
|
-
import type { FixtureDefinition } from '../fixtures/types.js';
|
|
36
|
-
|
|
37
|
-
export interface BootOptions {
|
|
38
|
-
discoverySource: DiscoverySource;
|
|
39
|
-
apps: DiscoveredApp[];
|
|
40
|
-
database?: DatabaseClientConfig;
|
|
41
|
-
server?: ServerConfig;
|
|
42
|
-
skipAutoSync?: boolean;
|
|
43
|
-
roles?: Array<{ config: RolesConfig; app: string }>;
|
|
44
|
-
jobs?: Array<{ name: string; config: JobConfig }>;
|
|
45
|
-
services?: ServiceDefinition[];
|
|
46
|
-
fixtures?: FixtureDefinition[];
|
|
47
|
-
worker?: JobWorkerConfig;
|
|
48
|
-
config?: Record<string, unknown>;
|
|
49
|
-
plugins?: PluginDefinition[];
|
|
50
|
-
pluginConfig?: Record<string, Record<string, unknown>>;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface BootResult {
|
|
54
|
-
registry: SchemaRegistry;
|
|
55
|
-
permissionRegistry: PermissionRegistry;
|
|
56
|
-
hookRegistry: HookRegistry;
|
|
57
|
-
jobRegistry: JobRegistry;
|
|
58
|
-
eventBus: EventBus;
|
|
59
|
-
serviceRegistry: ServiceRegistry;
|
|
60
|
-
fixtureRegistry: FixtureRegistry;
|
|
61
|
-
widgetRegistry: WidgetRegistry;
|
|
62
|
-
adapterRegistry: AdapterRegistry;
|
|
63
|
-
pluginLifecycle: PluginLifecycleManager;
|
|
64
|
-
frameworkContext?: FrameworkContext;
|
|
65
|
-
jobWorker?: JobWorker;
|
|
66
|
-
scheduleManager?: ScheduleManager;
|
|
67
|
-
db?: DatabaseClient;
|
|
68
|
-
server?: FastifyInstance;
|
|
69
|
-
pages?: Array<{ module: string; page: import('@rangka/shared').PageDefinition }>;
|
|
70
|
-
modules?: import('@rangka/shared').ModuleConfig[];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Boot sequence:
|
|
74
|
-
// 1. Discover and sort apps by dependency order
|
|
75
|
-
// 2. Load and merge schemas from all apps
|
|
76
|
-
// 3. Build registries (hooks, permissions, jobs, services, fixtures)
|
|
77
|
-
// 4. If database configured: connect, sync schema, seed, start workers
|
|
78
|
-
// 5. If server configured: create HTTP server and mount routes
|
|
79
|
-
export async function boot(options: BootOptions): Promise<BootResult> {
|
|
80
|
-
const sortedApps = await discoverAndSortApps(options);
|
|
81
|
-
|
|
82
|
-
// Load plugins and build adapter registry
|
|
83
|
-
const { adapterRegistry, lifecycleManager } = await loadPluginSystem(options);
|
|
84
|
-
await lifecycleManager.emit('beforeBoot');
|
|
85
|
-
|
|
86
|
-
const registry = buildSchemaRegistry(sortedApps);
|
|
87
|
-
const registries = buildRegistries(sortedApps, registry, options);
|
|
88
|
-
|
|
89
|
-
if (!options.database) {
|
|
90
|
-
await lifecycleManager.emit('afterBoot');
|
|
91
|
-
return { registry, ...registries, adapterRegistry, pluginLifecycle: lifecycleManager };
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const { db, frameworkContext, jobWorker, scheduleManager } = await initDatabase(
|
|
95
|
-
options,
|
|
96
|
-
registry,
|
|
97
|
-
registries.eventBus,
|
|
98
|
-
registries.jobRegistry,
|
|
99
|
-
registries.serviceRegistry,
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
if (!options.server) {
|
|
103
|
-
await lifecycleManager.emit('afterBoot');
|
|
104
|
-
return {
|
|
105
|
-
registry,
|
|
106
|
-
...registries,
|
|
107
|
-
adapterRegistry,
|
|
108
|
-
pluginLifecycle: lifecycleManager,
|
|
109
|
-
frameworkContext,
|
|
110
|
-
jobWorker,
|
|
111
|
-
scheduleManager,
|
|
112
|
-
db,
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const { server, pages, modules } = await initServer(
|
|
117
|
-
options,
|
|
118
|
-
sortedApps,
|
|
119
|
-
registry,
|
|
120
|
-
db,
|
|
121
|
-
registries,
|
|
122
|
-
adapterRegistry,
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
await lifecycleManager.emit('afterBoot');
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
registry,
|
|
129
|
-
...registries,
|
|
130
|
-
adapterRegistry,
|
|
131
|
-
pluginLifecycle: lifecycleManager,
|
|
132
|
-
frameworkContext,
|
|
133
|
-
jobWorker,
|
|
134
|
-
scheduleManager,
|
|
135
|
-
db,
|
|
136
|
-
server,
|
|
137
|
-
pages,
|
|
138
|
-
modules,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// --- Phase 1: Discovery ---
|
|
143
|
-
|
|
144
|
-
async function loadPluginSystem(options: BootOptions) {
|
|
145
|
-
if (!options.plugins || options.plugins.length === 0) {
|
|
146
|
-
return {
|
|
147
|
-
adapterRegistry: new AdapterRegistry(),
|
|
148
|
-
lifecycleManager: new PluginLifecycleManager(),
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return loadPlugins({
|
|
153
|
-
plugins: options.plugins,
|
|
154
|
-
config: options.pluginConfig,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
async function discoverAndSortApps(options: BootOptions): Promise<DiscoveredApp[]> {
|
|
159
|
-
await options.discoverySource.findRangkaPackages();
|
|
160
|
-
|
|
161
|
-
const coreApp = getCoreApp();
|
|
162
|
-
const userHasCoreApp = options.apps.some((a) => a.config.name === 'core');
|
|
163
|
-
const allApps = userHasCoreApp ? options.apps : [coreApp, ...options.apps];
|
|
164
|
-
|
|
165
|
-
const sorted = dependencySort(allApps.map((a) => a.config));
|
|
166
|
-
|
|
167
|
-
return sorted.map((config) => {
|
|
168
|
-
const app = allApps.find((a) => a.config.name === config.name);
|
|
169
|
-
if (!app) throw new Error(`App "${config.name}" not found in provided apps`);
|
|
170
|
-
return app;
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// --- Phase 2: Schema ---
|
|
175
|
-
|
|
176
|
-
function buildSchemaRegistry(sortedApps: DiscoveredApp[]): SchemaRegistry {
|
|
177
|
-
const loadResult = loadSchemas(sortedApps);
|
|
178
|
-
const mergeResult = mergeSchemas(loadResult);
|
|
179
|
-
|
|
180
|
-
const coreModels = getCoreModels();
|
|
181
|
-
const existingNames = new Set(mergeResult.models.map((m) => m.qualifiedName));
|
|
182
|
-
const missingCoreModels = coreModels.filter((m) => !existingNames.has(m.qualifiedName));
|
|
183
|
-
|
|
184
|
-
return new SchemaRegistry([...missingCoreModels, ...mergeResult.models]);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// --- Phase 3: Registries ---
|
|
188
|
-
|
|
189
|
-
interface Registries {
|
|
190
|
-
permissionRegistry: PermissionRegistry;
|
|
191
|
-
hookRegistry: HookRegistry;
|
|
192
|
-
jobRegistry: JobRegistry;
|
|
193
|
-
eventBus: EventBus;
|
|
194
|
-
serviceRegistry: ServiceRegistry;
|
|
195
|
-
fixtureRegistry: FixtureRegistry;
|
|
196
|
-
scopeRegistry: ScopeRegistry;
|
|
197
|
-
widgetRegistry: WidgetRegistry;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function buildRegistries(
|
|
201
|
-
sortedApps: DiscoveredApp[],
|
|
202
|
-
registry: SchemaRegistry,
|
|
203
|
-
options: BootOptions,
|
|
204
|
-
): Registries {
|
|
205
|
-
const allModules: import('@rangka/shared').ModuleConfig[] = [];
|
|
206
|
-
for (const app of sortedApps) {
|
|
207
|
-
if (app.modules) allModules.push(...app.modules);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return {
|
|
211
|
-
hookRegistry: buildHookRegistry(sortedApps),
|
|
212
|
-
permissionRegistry: buildPermissionRegistry(sortedApps, registry, options),
|
|
213
|
-
jobRegistry: buildJobRegistry(sortedApps, options),
|
|
214
|
-
eventBus: buildEventBus(sortedApps, options),
|
|
215
|
-
serviceRegistry: buildServiceRegistry(sortedApps, options),
|
|
216
|
-
fixtureRegistry: buildFixtureRegistry(sortedApps, options),
|
|
217
|
-
scopeRegistry: new ScopeRegistry(allModules, registry),
|
|
218
|
-
widgetRegistry: buildWidgetRegistry(sortedApps),
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function buildHookRegistry(sortedApps: DiscoveredApp[]): HookRegistry {
|
|
223
|
-
const hookRegistry = new HookRegistry();
|
|
224
|
-
for (const app of sortedApps) {
|
|
225
|
-
if (!app.hooks) continue;
|
|
226
|
-
for (const { model, hooks } of app.hooks) {
|
|
227
|
-
hookRegistry.register(model, hooks, app.config.name);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
return hookRegistry;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function buildPermissionRegistry(
|
|
234
|
-
sortedApps: DiscoveredApp[],
|
|
235
|
-
registry: SchemaRegistry,
|
|
236
|
-
options: BootOptions,
|
|
237
|
-
): PermissionRegistry {
|
|
238
|
-
const permissionRegistry = new PermissionRegistry();
|
|
239
|
-
|
|
240
|
-
// Administrator role gets full access to all models
|
|
241
|
-
const adminModels: Record<string, Record<string, boolean>> = {};
|
|
242
|
-
for (const model of registry.getAllModels()) {
|
|
243
|
-
adminModels[model.qualifiedName] = {
|
|
244
|
-
create: true,
|
|
245
|
-
read: true,
|
|
246
|
-
write: true,
|
|
247
|
-
delete: true,
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
permissionRegistry.registerRoles(
|
|
251
|
-
{ Administrator: { label: 'Administrator', models: adminModels } },
|
|
252
|
-
'core',
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
// Register roles discovered from each app's modules/<name>/roles.ts
|
|
256
|
-
for (const app of sortedApps) {
|
|
257
|
-
if (!app.roles) continue;
|
|
258
|
-
for (const { config, app: appName } of app.roles) {
|
|
259
|
-
permissionRegistry.registerRoles(config, appName);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Register roles passed explicitly via boot options (override/legacy)
|
|
264
|
-
if (options.roles) {
|
|
265
|
-
for (const { config, app } of options.roles) {
|
|
266
|
-
permissionRegistry.registerRoles(config, app);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
return permissionRegistry;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function buildJobRegistry(sortedApps: DiscoveredApp[], options: BootOptions): JobRegistry {
|
|
274
|
-
const jobRegistry = new JobRegistry();
|
|
275
|
-
|
|
276
|
-
for (const app of sortedApps) {
|
|
277
|
-
if (!app.jobs) continue;
|
|
278
|
-
for (const { name, config } of app.jobs) {
|
|
279
|
-
jobRegistry.register(name, config);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
if (options.jobs) {
|
|
284
|
-
for (const { name, config } of options.jobs) {
|
|
285
|
-
if (!jobRegistry.has(name)) {
|
|
286
|
-
jobRegistry.register(name, config);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return jobRegistry;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function buildEventBus(sortedApps: DiscoveredApp[], options: BootOptions): EventBus {
|
|
295
|
-
const eventBus = new EventBus();
|
|
296
|
-
const jobRegistry = buildJobRegistry(sortedApps, options);
|
|
297
|
-
|
|
298
|
-
for (const job of jobRegistry.getAll()) {
|
|
299
|
-
if (job.name.startsWith('__event:')) {
|
|
300
|
-
const eventName = job.name.slice('__event:'.length);
|
|
301
|
-
eventBus.on(eventName, async (payload: unknown) => {
|
|
302
|
-
await job.config.handler(payload, {} as any);
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
return eventBus;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function buildServiceRegistry(sortedApps: DiscoveredApp[], options: BootOptions): ServiceRegistry {
|
|
311
|
-
const serviceRegistry = new ServiceRegistry();
|
|
312
|
-
|
|
313
|
-
for (const app of sortedApps) {
|
|
314
|
-
if (!app.services) continue;
|
|
315
|
-
for (const def of app.services) {
|
|
316
|
-
if (!serviceRegistry.has(def.name)) {
|
|
317
|
-
serviceRegistry.register(def);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (options.services) {
|
|
323
|
-
for (const def of options.services) {
|
|
324
|
-
if (!serviceRegistry.has(def.name)) {
|
|
325
|
-
serviceRegistry.register(def);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
serviceRegistry.detectCircularDependencies();
|
|
331
|
-
return serviceRegistry;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function buildFixtureRegistry(sortedApps: DiscoveredApp[], options: BootOptions): FixtureRegistry {
|
|
335
|
-
const fixtureRegistry = new FixtureRegistry();
|
|
336
|
-
|
|
337
|
-
for (const app of sortedApps) {
|
|
338
|
-
if (!app.fixtures) continue;
|
|
339
|
-
for (const def of app.fixtures) {
|
|
340
|
-
fixtureRegistry.register(def);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (options.fixtures) {
|
|
345
|
-
for (const def of options.fixtures) {
|
|
346
|
-
fixtureRegistry.register(def);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
return fixtureRegistry;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// --- Phase 4: Database ---
|
|
354
|
-
|
|
355
|
-
async function initDatabase(
|
|
356
|
-
options: BootOptions,
|
|
357
|
-
registry: SchemaRegistry,
|
|
358
|
-
eventBus: EventBus,
|
|
359
|
-
jobRegistry: JobRegistry,
|
|
360
|
-
serviceRegistry: ServiceRegistry,
|
|
361
|
-
) {
|
|
362
|
-
const db = new DatabaseClient(options.database!, registry);
|
|
363
|
-
|
|
364
|
-
try {
|
|
365
|
-
await db.verifyConnection();
|
|
366
|
-
} catch (err) {
|
|
367
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
368
|
-
throw new Error(`Boot failed: ${message}`, { cause: err });
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
eventBus.setDb(db.kysely);
|
|
372
|
-
|
|
373
|
-
if (!options.skipAutoSync) {
|
|
374
|
-
await autoSync(registry, db.kysely);
|
|
375
|
-
await seedCoreData(db);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const frameworkContext = createFrameworkContext({
|
|
379
|
-
db,
|
|
380
|
-
schema: registry,
|
|
381
|
-
eventBus,
|
|
382
|
-
serviceRegistry,
|
|
383
|
-
config: options.config ?? {},
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
let jobWorker: JobWorker | undefined;
|
|
387
|
-
let scheduleManager: ScheduleManager | undefined;
|
|
388
|
-
|
|
389
|
-
if (options.worker?.enabled !== false) {
|
|
390
|
-
jobWorker = new JobWorker(db.kysely, jobRegistry, frameworkContext, options.worker);
|
|
391
|
-
scheduleManager = new ScheduleManager(db.kysely, jobRegistry);
|
|
392
|
-
await scheduleManager.syncSchedules();
|
|
393
|
-
jobWorker.start();
|
|
394
|
-
scheduleManager.start();
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
return { db, frameworkContext, jobWorker, scheduleManager };
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// --- Phase 5: Server ---
|
|
401
|
-
|
|
402
|
-
async function initServer(
|
|
403
|
-
options: BootOptions,
|
|
404
|
-
sortedApps: DiscoveredApp[],
|
|
405
|
-
registry: SchemaRegistry,
|
|
406
|
-
db: DatabaseClient,
|
|
407
|
-
registries: Registries,
|
|
408
|
-
adapterRegistry?: AdapterRegistry,
|
|
409
|
-
): Promise<{
|
|
410
|
-
server: FastifyInstance;
|
|
411
|
-
pages: Array<{ module: string; page: import('@rangka/shared').PageDefinition }>;
|
|
412
|
-
modules: import('@rangka/shared').ModuleConfig[];
|
|
413
|
-
}> {
|
|
414
|
-
const moduleTags = [...registry.getModelsByModule().keys()].map((name) => ({ name }));
|
|
415
|
-
const server = await createServer({ ...options.server!, tags: moduleTags });
|
|
416
|
-
|
|
417
|
-
const allPages: Array<{ module: string; page: import('@rangka/shared').PageDefinition }> = [];
|
|
418
|
-
const allModules: import('@rangka/shared').ModuleConfig[] = [];
|
|
419
|
-
|
|
420
|
-
for (const app of sortedApps) {
|
|
421
|
-
if (app.pages) allPages.push(...app.pages);
|
|
422
|
-
if (app.modules) allModules.push(...app.modules);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
generateRoutes(server, registry, db, {
|
|
426
|
-
permissionRegistry: registries.permissionRegistry,
|
|
427
|
-
hookRegistry: registries.hookRegistry,
|
|
428
|
-
serviceRegistry: registries.serviceRegistry,
|
|
429
|
-
eventBus: registries.eventBus,
|
|
430
|
-
scopeRegistry: registries.scopeRegistry,
|
|
431
|
-
config: options.config ?? {},
|
|
432
|
-
pages: allPages.length > 0 ? allPages : undefined,
|
|
433
|
-
modules: allModules.length > 0 ? allModules : undefined,
|
|
434
|
-
widgets: registries.widgetRegistry.getAll(),
|
|
435
|
-
adapterRegistry,
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
for (const app of sortedApps) {
|
|
439
|
-
if (!app.apiDefinitions) continue;
|
|
440
|
-
for (const def of app.apiDefinitions) {
|
|
441
|
-
server.route({ method: def.method, url: def.path, handler: def.handler });
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
return { server, pages: allPages, modules: allModules };
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
function buildWidgetRegistry(sortedApps: DiscoveredApp[]): WidgetRegistry {
|
|
449
|
-
const widgetRegistry = new WidgetRegistry();
|
|
450
|
-
for (const app of sortedApps) {
|
|
451
|
-
if (!app.widgets) continue;
|
|
452
|
-
for (const widget of app.widgets) {
|
|
453
|
-
widgetRegistry.register(widget);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
return widgetRegistry;
|
|
457
|
-
}
|
package/src/boot/page-utils.ts
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import type { PageDefinition, WidgetNode } from '@rangka/shared';
|
|
2
|
-
|
|
3
|
-
export interface PageValidationWarning {
|
|
4
|
-
pageKey: string;
|
|
5
|
-
location: string;
|
|
6
|
-
message: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Collect all distinct model names referenced as data sources within a page body.
|
|
11
|
-
*/
|
|
12
|
-
export function extractSourceModels(page: PageDefinition): string[] {
|
|
13
|
-
const models = new Set<string>();
|
|
14
|
-
|
|
15
|
-
for (const node of page.body) {
|
|
16
|
-
collectModelsFromWidgetNode(node, models);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return [...models];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Validate that every source model referenced by pages actually exists
|
|
24
|
-
* in the known model set. Returns warnings for unresolved references.
|
|
25
|
-
*/
|
|
26
|
-
export function validatePageSources(
|
|
27
|
-
pages: Array<{ module: string; page: PageDefinition }>,
|
|
28
|
-
knownModels: Set<string>,
|
|
29
|
-
): PageValidationWarning[] {
|
|
30
|
-
const warnings: PageValidationWarning[] = [];
|
|
31
|
-
|
|
32
|
-
for (const { page } of pages) {
|
|
33
|
-
for (let i = 0; i < page.body.length; i++) {
|
|
34
|
-
checkWidgetNodeSources(page.body[i], `body[${i}]`, page.key, knownModels, warnings);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return warnings;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/** Detect pages that share the same key across different modules. */
|
|
42
|
-
export function detectDuplicatePageKeys(
|
|
43
|
-
pages: Array<{ module: string; page: PageDefinition }>,
|
|
44
|
-
): PageValidationWarning[] {
|
|
45
|
-
const warnings: PageValidationWarning[] = [];
|
|
46
|
-
const seenKeyInModule = new Map<string, string>();
|
|
47
|
-
|
|
48
|
-
for (const { module, page } of pages) {
|
|
49
|
-
const previousModule = seenKeyInModule.get(page.key);
|
|
50
|
-
if (previousModule) {
|
|
51
|
-
warnings.push({
|
|
52
|
-
pageKey: page.key,
|
|
53
|
-
location: `modules/${module}/pages`,
|
|
54
|
-
message: `Duplicate page key "${page.key}" (also defined in ${previousModule})`,
|
|
55
|
-
});
|
|
56
|
-
} else {
|
|
57
|
-
seenKeyInModule.set(page.key, `modules/${module}/pages`);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return warnings;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function collectModelsFromWidgetNode(node: WidgetNode, models: Set<string>): void {
|
|
65
|
-
if (node.source?.model) {
|
|
66
|
-
models.add(node.source.model);
|
|
67
|
-
}
|
|
68
|
-
if (node.bind?.model?.name) {
|
|
69
|
-
models.add(node.bind.model.name);
|
|
70
|
-
}
|
|
71
|
-
if (node.children) {
|
|
72
|
-
for (const child of node.children) {
|
|
73
|
-
collectModelsFromWidgetNode(child, models);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function checkWidgetNodeSources(
|
|
79
|
-
node: WidgetNode,
|
|
80
|
-
path: string,
|
|
81
|
-
pageKey: string,
|
|
82
|
-
knownModels: Set<string>,
|
|
83
|
-
warnings: PageValidationWarning[],
|
|
84
|
-
): void {
|
|
85
|
-
if (node.source?.model && !knownModels.has(node.source.model)) {
|
|
86
|
-
warnings.push({
|
|
87
|
-
pageKey,
|
|
88
|
-
location: path,
|
|
89
|
-
message: `Unresolved source model "${node.source.model}"`,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
if (node.bind?.model?.name && !knownModels.has(node.bind.model.name)) {
|
|
93
|
-
warnings.push({
|
|
94
|
-
pageKey,
|
|
95
|
-
location: path,
|
|
96
|
-
message: `Unresolved source model "${node.bind.model.name}"`,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
if (node.children) {
|
|
100
|
-
for (let i = 0; i < node.children.length; i++) {
|
|
101
|
-
checkWidgetNodeSources(
|
|
102
|
-
node.children[i],
|
|
103
|
-
`${path}.children[${i}]`,
|
|
104
|
-
pageKey,
|
|
105
|
-
knownModels,
|
|
106
|
-
warnings,
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|