@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.
Files changed (197) hide show
  1. package/package.json +6 -2
  2. package/.claude/skills/extend-core/SKILL.md +0 -133
  3. package/.turbo/turbo-build.log +0 -4
  4. package/CHANGELOG.md +0 -18
  5. package/CLAUDE.md +0 -180
  6. package/src/__tests__/coerce.test.ts +0 -154
  7. package/src/__tests__/context.test.ts +0 -111
  8. package/src/__tests__/helpers.ts +0 -21
  9. package/src/__tests__/index.test.ts +0 -7
  10. package/src/__tests__/widgets.test.ts +0 -197
  11. package/src/api/__tests__/handlers.test.ts +0 -389
  12. package/src/api/__tests__/include-resolver.test.ts +0 -393
  13. package/src/api/__tests__/middleware.test.ts +0 -100
  14. package/src/api/__tests__/openapi-schema.test.ts +0 -210
  15. package/src/api/__tests__/query-parser.test.ts +0 -291
  16. package/src/api/__tests__/route-generator.test.ts +0 -137
  17. package/src/api/__tests__/server.test.ts +0 -73
  18. package/src/api/__tests__/swagger.test.ts +0 -166
  19. package/src/api/handlers.ts +0 -274
  20. package/src/api/include-resolver.ts +0 -27
  21. package/src/api/index.ts +0 -4
  22. package/src/api/meta-handler.ts +0 -254
  23. package/src/api/openapi-schema.ts +0 -99
  24. package/src/api/query-parser.ts +0 -315
  25. package/src/api/route-generator.ts +0 -448
  26. package/src/api/server.ts +0 -147
  27. package/src/api/types.ts +0 -16
  28. package/src/audit/__tests__/audit.test.ts +0 -144
  29. package/src/audit/index.ts +0 -3
  30. package/src/audit/record.ts +0 -69
  31. package/src/audit/tables.ts +0 -48
  32. package/src/audit/types.ts +0 -26
  33. package/src/auth/__tests__/core-module.test.ts +0 -54
  34. package/src/auth/__tests__/debug.test.ts +0 -47
  35. package/src/auth/__tests__/field-permissions.test.ts +0 -245
  36. package/src/auth/__tests__/integration.test.ts +0 -208
  37. package/src/auth/__tests__/meta-boot.test.ts +0 -538
  38. package/src/auth/__tests__/model-permissions.test.ts +0 -205
  39. package/src/auth/__tests__/password.test.ts +0 -29
  40. package/src/auth/__tests__/permission-registry.test.ts +0 -313
  41. package/src/auth/__tests__/scope-hook.test.ts +0 -509
  42. package/src/auth/__tests__/scope-registry.test.ts +0 -297
  43. package/src/auth/__tests__/scopes.test.ts +0 -66
  44. package/src/auth/__tests__/session.test.ts +0 -214
  45. package/src/auth/core-models.ts +0 -52
  46. package/src/auth/core-module.ts +0 -59
  47. package/src/auth/debug.ts +0 -157
  48. package/src/auth/field-permissions.ts +0 -116
  49. package/src/auth/index.ts +0 -37
  50. package/src/auth/model-permissions.ts +0 -59
  51. package/src/auth/password.ts +0 -22
  52. package/src/auth/permission-registry.ts +0 -171
  53. package/src/auth/scope-filters.ts +0 -11
  54. package/src/auth/scope-registry.ts +0 -121
  55. package/src/auth/scopes.ts +0 -146
  56. package/src/auth/seed.ts +0 -44
  57. package/src/auth/session.ts +0 -178
  58. package/src/auth/types.ts +0 -50
  59. package/src/boot/__tests__/page-scanning.test.ts +0 -170
  60. package/src/boot/__tests__/page-utils.test.ts +0 -225
  61. package/src/boot/__tests__/project-scanner.test.ts +0 -88
  62. package/src/boot/dependency-sort.ts +0 -82
  63. package/src/boot/discovery.ts +0 -85
  64. package/src/boot/index.ts +0 -457
  65. package/src/boot/page-utils.ts +0 -110
  66. package/src/boot/project-scanner.ts +0 -397
  67. package/src/boot/schema-loader.ts +0 -26
  68. package/src/boot/schema-merger.ts +0 -125
  69. package/src/boot/traits.ts +0 -25
  70. package/src/boot/types.ts +0 -73
  71. package/src/context.ts +0 -105
  72. package/src/db/__tests__/cascade-delete.test.ts +0 -182
  73. package/src/db/__tests__/desired-state.test.ts +0 -136
  74. package/src/db/__tests__/diff-engine.test.ts +0 -635
  75. package/src/db/__tests__/field-mapper.test.ts +0 -355
  76. package/src/db/__tests__/introspect.test.ts +0 -70
  77. package/src/db/__tests__/search-filter.test.ts +0 -45
  78. package/src/db/__tests__/sequence.test.ts +0 -221
  79. package/src/db/auto-sync.ts +0 -133
  80. package/src/db/client.ts +0 -147
  81. package/src/db/desired-state.ts +0 -98
  82. package/src/db/diff-engine.ts +0 -305
  83. package/src/db/field-mapper.ts +0 -504
  84. package/src/db/filter-applier.ts +0 -89
  85. package/src/db/include-resolver.ts +0 -40
  86. package/src/db/index.ts +0 -23
  87. package/src/db/introspect.ts +0 -265
  88. package/src/db/model-include-resolver.ts +0 -327
  89. package/src/db/model-ops.ts +0 -281
  90. package/src/db/scope-enforcer.ts +0 -37
  91. package/src/db/types.ts +0 -98
  92. package/src/errors.ts +0 -41
  93. package/src/events/__tests__/bus.test.ts +0 -105
  94. package/src/events/bus.ts +0 -89
  95. package/src/events/index.ts +0 -2
  96. package/src/events/types.ts +0 -9
  97. package/src/external-model/__tests__/computed-fields.test.ts +0 -106
  98. package/src/external-model/__tests__/field-mapper.test.ts +0 -160
  99. package/src/external-model/__tests__/in-memory-ops.test.ts +0 -247
  100. package/src/external-model/__tests__/mutation-executor.test.ts +0 -160
  101. package/src/external-model/__tests__/query-executor.test.ts +0 -284
  102. package/src/external-model/__tests__/schema-converter.test.ts +0 -174
  103. package/src/external-model/computed-fields.ts +0 -15
  104. package/src/external-model/define.ts +0 -5
  105. package/src/external-model/external-model-ops.ts +0 -108
  106. package/src/external-model/field-mapper.ts +0 -66
  107. package/src/external-model/in-memory-ops.ts +0 -107
  108. package/src/external-model/index.ts +0 -7
  109. package/src/external-model/mutation-executor.ts +0 -71
  110. package/src/external-model/query-executor.ts +0 -100
  111. package/src/external-model/schema-converter.ts +0 -53
  112. package/src/external-model/types.ts +0 -32
  113. package/src/fixtures/__tests__/fixtures.test.ts +0 -203
  114. package/src/fixtures/index.ts +0 -10
  115. package/src/fixtures/loader.ts +0 -196
  116. package/src/fixtures/registry.ts +0 -125
  117. package/src/fixtures/types.ts +0 -33
  118. package/src/helpers/assert-ownership.ts +0 -19
  119. package/src/helpers/coerce.ts +0 -28
  120. package/src/helpers/stamping.ts +0 -28
  121. package/src/helpers/validation.ts +0 -14
  122. package/src/hooks/__tests__/context.test.ts +0 -73
  123. package/src/hooks/__tests__/executor.test.ts +0 -433
  124. package/src/hooks/__tests__/middleware.test.ts +0 -224
  125. package/src/hooks/__tests__/registry.test.ts +0 -50
  126. package/src/hooks/context.ts +0 -89
  127. package/src/hooks/errors.ts +0 -11
  128. package/src/hooks/executor.ts +0 -115
  129. package/src/hooks/index.ts +0 -10
  130. package/src/hooks/middleware.ts +0 -220
  131. package/src/hooks/registry.ts +0 -20
  132. package/src/hooks/types.ts +0 -32
  133. package/src/index.ts +0 -172
  134. package/src/jobs/__tests__/enqueue.test.ts +0 -77
  135. package/src/jobs/__tests__/integration.test.ts +0 -71
  136. package/src/jobs/__tests__/registry.test.ts +0 -103
  137. package/src/jobs/__tests__/scheduler.test.ts +0 -92
  138. package/src/jobs/__tests__/worker-execution.test.ts +0 -202
  139. package/src/jobs/__tests__/worker.test.ts +0 -119
  140. package/src/jobs/enqueue.ts +0 -93
  141. package/src/jobs/index.ts +0 -14
  142. package/src/jobs/registry.ts +0 -92
  143. package/src/jobs/scheduler.ts +0 -205
  144. package/src/jobs/tables.ts +0 -132
  145. package/src/jobs/types.ts +0 -62
  146. package/src/jobs/worker.ts +0 -272
  147. package/src/model-api/__tests__/cross-boundary-includes.test.ts +0 -366
  148. package/src/model-api/__tests__/extended-api.test.ts +0 -244
  149. package/src/model-api/__tests__/filter-applier.test.ts +0 -177
  150. package/src/model-api/__tests__/filter-translator.test.ts +0 -186
  151. package/src/model-api/__tests__/include-resolver.test.ts +0 -226
  152. package/src/model-api/__tests__/model-access.test.ts +0 -284
  153. package/src/model-api/__tests__/query-builder.test.ts +0 -224
  154. package/src/model-api/__tests__/scope-enforcer.test.ts +0 -268
  155. package/src/model-api/field-access.ts +0 -28
  156. package/src/model-api/filter-applier.ts +0 -1
  157. package/src/model-api/filter-translator.ts +0 -67
  158. package/src/model-api/include-resolver.ts +0 -2
  159. package/src/model-api/index.ts +0 -86
  160. package/src/model-api/query-builder.ts +0 -155
  161. package/src/model-api/scope-enforcer.ts +0 -3
  162. package/src/model-api/types.ts +0 -139
  163. package/src/plugins/__tests__/adapter-registry.test.ts +0 -92
  164. package/src/plugins/__tests__/lifecycle.test.ts +0 -96
  165. package/src/plugins/__tests__/loader.test.ts +0 -273
  166. package/src/plugins/__tests__/validator.test.ts +0 -275
  167. package/src/plugins/adapter-registry.ts +0 -42
  168. package/src/plugins/define.ts +0 -5
  169. package/src/plugins/index.ts +0 -28
  170. package/src/plugins/lifecycle.ts +0 -27
  171. package/src/plugins/loader.ts +0 -126
  172. package/src/plugins/types.ts +0 -76
  173. package/src/plugins/validator.ts +0 -141
  174. package/src/schema/__tests__/registry-models-by-module.test.ts +0 -58
  175. package/src/schema/registry.ts +0 -93
  176. package/src/schema/relationships.ts +0 -93
  177. package/src/schema/types.ts +0 -43
  178. package/src/services/__tests__/integration.test.ts +0 -63
  179. package/src/services/__tests__/registry.test.ts +0 -175
  180. package/src/services/index.ts +0 -13
  181. package/src/services/registry.ts +0 -156
  182. package/src/services/types.ts +0 -27
  183. package/src/validation/__tests__/field-validator.test.ts +0 -195
  184. package/src/validation/field-validator.ts +0 -113
  185. package/src/validation/index.ts +0 -1
  186. package/src/widgets/index.ts +0 -3
  187. package/src/widgets/slot-validator.ts +0 -87
  188. package/src/widgets/widget-registry.ts +0 -32
  189. package/tests/boot.test.ts +0 -323
  190. package/tests/dependency-sort.test.ts +0 -99
  191. package/tests/discovery.test.ts +0 -126
  192. package/tests/registry.test.ts +0 -216
  193. package/tests/schema-loader.test.ts +0 -52
  194. package/tests/schema-merger.test.ts +0 -180
  195. package/tsconfig.json +0 -9
  196. package/tsconfig.tsbuildinfo +0 -1
  197. package/vitest.config.ts +0 -14
@@ -1,89 +0,0 @@
1
- import type { Kysely } from 'kysely';
2
- import type { FrameworkContext } from '@rangka/shared';
3
- import type { SchemaRegistry } from '../schema/registry.js';
4
- import type { RequestContext } from '../auth/types.js';
5
- import type { EventBus } from '../events/bus.js';
6
- import type { ServiceRegistry } from '../services/registry.js';
7
- import type { AdapterRegistry } from '../plugins/adapter-registry.js';
8
- import { enqueue } from '../jobs/enqueue.js';
9
- import { createModelAccess } from '../model-api/index.js';
10
-
11
- export interface HookContextOptions {
12
- trx: unknown;
13
- schema: SchemaRegistry;
14
- auth: RequestContext;
15
- eventBus?: EventBus;
16
- serviceRegistry?: ServiceRegistry;
17
- config?: Record<string, unknown>;
18
- adapterRegistry?: AdapterRegistry;
19
- }
20
-
21
- /**
22
- * Build a full FrameworkContext for hooks, scoped to the current transaction and auth.
23
- */
24
- export function createHookContext(opts: HookContextOptions): FrameworkContext {
25
- const { trx, schema, auth, eventBus, serviceRegistry, config = {}, adapterRegistry } = opts;
26
-
27
- const transaction = trx as Kysely<unknown>;
28
-
29
- const events = {
30
- emit: async (event: string, payload: unknown) => {
31
- if (eventBus) {
32
- await eventBus.emitWithTrx(event, payload, transaction);
33
- }
34
- },
35
- on: (event: string, handler: (payload: unknown) => Promise<void>) => {
36
- eventBus?.on(event, handler);
37
- },
38
- };
39
-
40
- const enqueueJob = async (
41
- job: string,
42
- data: unknown,
43
- opts?: { delay?: number; unique?: boolean; uniqueKey?: string },
44
- ) => {
45
- await enqueue(transaction, job, data, opts);
46
- };
47
-
48
- /* eslint-disable @typescript-eslint/no-explicit-any */
49
- const models = createModelAccess({
50
- db: transaction as any,
51
- registry: schema,
52
- auth,
53
- adapterRegistry,
54
- });
55
- /* eslint-enable @typescript-eslint/no-explicit-any */
56
-
57
- const service = (name: string) => {
58
- if (!serviceRegistry) {
59
- throw new Error('ServiceRegistry not available in this context');
60
- }
61
- return serviceRegistry.get(name, {
62
- db: transaction,
63
- schema,
64
- enqueue: enqueueJob,
65
- events,
66
- config,
67
- models,
68
- auth: { user: auth.user ?? null, roles: auth.roles ?? [] },
69
- scope: auth.scopeFilters?.[0]?.value ?? null,
70
- });
71
- };
72
-
73
- return {
74
- db: transaction,
75
- schema,
76
- auth: {
77
- user: auth.user ?? null,
78
- roles: auth.roles ?? [],
79
- },
80
- scope: auth.scopeFilters?.[0]?.value ?? null,
81
- models,
82
- events,
83
- config,
84
- service,
85
- enqueue: enqueueJob,
86
- notify: () => {},
87
- email: { send: async () => {} },
88
- };
89
- }
@@ -1,11 +0,0 @@
1
- export class ValidationError extends Error {
2
- public readonly field?: string;
3
- public readonly code: string;
4
-
5
- constructor(field: string, message: string) {
6
- super(message);
7
- this.name = 'ValidationError';
8
- this.field = field;
9
- this.code = 'VALIDATION_ERROR';
10
- }
11
- }
@@ -1,115 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import type { Kysely } from 'kysely';
3
- import type { HooksConfig } from '@rangka/shared';
4
- import type { HookChain, HookContext, HookDocument } from './types.js';
5
- import type { SchemaRegistry } from '../schema/registry.js';
6
- import type { RequestContext } from '../auth/types.js';
7
- import type { EventBus } from '../events/bus.js';
8
- import type { ServiceRegistry } from '../services/registry.js';
9
- import { createHookContext } from './context.js';
10
-
11
- export type HookOperation = 'create' | 'update' | 'delete' | 'submit' | 'cancel';
12
-
13
- export interface ExecutePipelineOptions {
14
- model: string;
15
- operation: HookOperation;
16
- chain: HookChain;
17
- doc: HookDocument;
18
- db: Kysely<any>;
19
- schema: SchemaRegistry;
20
- auth: RequestContext;
21
- eventBus?: EventBus;
22
- serviceRegistry?: ServiceRegistry;
23
- config?: Record<string, unknown>;
24
- execute: (doc: HookDocument, trx: Kysely<any>) => Promise<HookDocument>;
25
- }
26
-
27
- /**
28
- * Run the full hook pipeline for a mutation operation.
29
- * Transaction covers: validate -> beforeSave -> before{Op} -> execute -> after{Op}
30
- * afterSave runs AFTER the transaction commits (cannot roll back the write).
31
- */
32
- export async function executeHookPipeline(opts: ExecutePipelineOptions): Promise<HookDocument> {
33
- const { chain, operation, db, schema, auth, eventBus, serviceRegistry, config, execute } = opts;
34
- const doc = { ...opts.doc };
35
-
36
- const beforeHookKey = `before${capitalize(operation)}` as keyof HooksConfig;
37
- const afterHookKey = `after${capitalize(operation)}` as keyof HooksConfig;
38
-
39
- const result = await db.transaction().execute(async (trx) => {
40
- const ctx: HookContext = createHookContext({
41
- trx,
42
- schema,
43
- auth,
44
- eventBus,
45
- serviceRegistry,
46
- config,
47
- });
48
-
49
- // Step 1: Run synchronous validators (skip for delete)
50
- if (operation !== 'delete') {
51
- runValidators(chain, doc);
52
- }
53
-
54
- // Step 2: Run beforeSave hooks (only for create/update)
55
- if (operation === 'create' || operation === 'update') {
56
- await runHooksForKey(chain, 'beforeSave', doc, ctx);
57
- }
58
-
59
- // Step 3: Run operation-specific before hooks (e.g. beforeCreate)
60
- await runHooksForKey(chain, beforeHookKey, doc, ctx);
61
-
62
- // Step 4: Execute the actual database operation
63
- const txResult = await execute(doc, trx);
64
-
65
- // Step 5: Run operation-specific after hooks (e.g. afterCreate)
66
- await runHooksForKey(chain, afterHookKey, txResult, ctx);
67
-
68
- return txResult;
69
- });
70
-
71
- // Step 6: Run afterSave hooks OUTSIDE the transaction (data already committed)
72
- if (operation === 'create' || operation === 'update') {
73
- const ctx: HookContext = createHookContext({
74
- trx: db,
75
- schema,
76
- auth,
77
- eventBus,
78
- serviceRegistry,
79
- config,
80
- });
81
- await runHooksForKey(chain, 'afterSave', result, ctx);
82
- }
83
-
84
- return result;
85
- }
86
-
87
- // --- Helpers ---
88
-
89
- function capitalize(s: string): string {
90
- return s.charAt(0).toUpperCase() + s.slice(1);
91
- }
92
-
93
- /** Run all synchronous validate hooks in the chain. */
94
- function runValidators(chain: HookChain, doc: HookDocument): void {
95
- for (const entry of chain.entries) {
96
- if (entry.hooks.validate) {
97
- entry.hooks.validate(doc);
98
- }
99
- }
100
- }
101
-
102
- /** Run all hooks for a given key across the chain entries. */
103
- async function runHooksForKey(
104
- chain: HookChain,
105
- key: keyof HooksConfig,
106
- doc: HookDocument,
107
- ctx: HookContext,
108
- ): Promise<void> {
109
- for (const entry of chain.entries) {
110
- const hook = entry.hooks[key] as ((doc: HookDocument, ctx: any) => Promise<void>) | undefined;
111
- if (hook) {
112
- await hook(doc, ctx);
113
- }
114
- }
115
- }
@@ -1,10 +0,0 @@
1
- export { HookRegistry } from './registry.js';
2
- export { ValidationError } from './errors.js';
3
- export { executeHookPipeline } from './executor.js';
4
- export { createHookContext } from './context.js';
5
- export {
6
- withHooksCreate,
7
- withHooksUpdate,
8
- withHooksDelete,
9
- } from './middleware.js';
10
- export type { HookChain, HookEntry, HookLifecycle, HookContext, HookDocument } from './types.js';
@@ -1,220 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-explicit-any */
2
- import type { FastifyRequest, FastifyReply } from 'fastify';
3
- import type { Kysely } from 'kysely';
4
- import type { ResolvedModel } from '../schema/types.js';
5
- import type { SchemaRegistry } from '../schema/registry.js';
6
- import type { HookRegistry } from './registry.js';
7
- import type { HookDocument } from './types.js';
8
- import type { ServiceRegistry } from '../services/registry.js';
9
- import type { EventBus } from '../events/bus.js';
10
- import { executeHookPipeline, type HookOperation } from './executor.js';
11
- import { ValidationError } from './errors.js';
12
- import { getAuthContext } from '../auth/session.js';
13
- import { validateFields } from '../validation/field-validator.js';
14
- import { assertOwnership } from '../helpers/assert-ownership.js';
15
- import { stampCreate, stampUpdate } from '../helpers/stamping.js';
16
- import { findMissingRequiredFields } from '../helpers/validation.js';
17
- import { BadRequestError, NotFoundError } from '../errors.js';
18
- import type { ModelOps } from '../model-api/types.js';
19
- import { ModelQueryBuilder } from '../model-api/query-builder.js';
20
-
21
- // --- Types ---
22
-
23
- interface WithHooksContext {
24
- model: ResolvedModel;
25
- registry: SchemaRegistry;
26
- db: Kysely<any>;
27
- ops: ModelOps;
28
- hookRegistry: HookRegistry;
29
- serviceRegistry?: ServiceRegistry;
30
- eventBus?: EventBus;
31
- config?: Record<string, unknown>;
32
- }
33
-
34
- // --- Shared helpers (internal) ---
35
-
36
- function parseRequestBody(request: FastifyRequest): Record<string, unknown> {
37
- const body = request.body as Record<string, unknown> | undefined;
38
- if (!body || typeof body !== 'object') {
39
- throw new BadRequestError('VALIDATION_ERROR', 'Request body is required');
40
- }
41
- return body;
42
- }
43
-
44
- async function findRecordOrNotFound(
45
- ctx: WithHooksContext,
46
- id: string,
47
- ): Promise<Record<string, unknown>> {
48
- const record = await new ModelQueryBuilder(ctx.ops, ctx.model, ctx.registry)
49
- .filter({ id })
50
- .first();
51
-
52
- if (!record) {
53
- throw new NotFoundError(`Record not found: ${id}`);
54
- }
55
- return record;
56
- }
57
-
58
- function handleHookError(err: unknown): never {
59
- if (err instanceof ValidationError) {
60
- throw new BadRequestError(err.code, err.message, err.field ? { field: err.field } : undefined);
61
- }
62
- if (err instanceof Error) {
63
- throw new BadRequestError('VALIDATION_ERROR', err.message);
64
- }
65
- throw err;
66
- }
67
-
68
- function buildPipelineOptions(
69
- ctx: WithHooksContext,
70
- operation: HookOperation,
71
- doc: HookDocument,
72
- auth: any,
73
- execute: (doc: HookDocument, trx: Kysely<unknown>) => Promise<HookDocument>,
74
- ) {
75
- return {
76
- model: ctx.model.qualifiedName,
77
- operation,
78
- chain: ctx.hookRegistry.getChain(ctx.model.qualifiedName)!,
79
- doc,
80
- db: ctx.db,
81
- schema: ctx.registry,
82
- auth,
83
- eventBus: ctx.eventBus,
84
- serviceRegistry: ctx.serviceRegistry,
85
- config: ctx.config,
86
- execute,
87
- };
88
- }
89
-
90
- // --- Exported middleware factories ---
91
-
92
- export function withHooksCreate(ctx: WithHooksContext) {
93
- return async (request: FastifyRequest, reply: FastifyReply) => {
94
- const body = parseRequestBody(request);
95
-
96
- const missingFields = findMissingRequiredFields(ctx.model, body);
97
- if (missingFields.length > 0) {
98
- throw new BadRequestError(
99
- 'VALIDATION_ERROR',
100
- `Missing required fields: ${missingFields.join(', ')}`,
101
- missingFields,
102
- );
103
- }
104
-
105
- const fieldViolations = validateFields(ctx.model, body, 'create');
106
- if (fieldViolations.length > 0) {
107
- throw new BadRequestError(
108
- 'VALIDATION_ERROR',
109
- fieldViolations.map((v) => v.message).join('; '),
110
- fieldViolations,
111
- );
112
- }
113
-
114
- const auth = getAuthContext(request);
115
- stampCreate(body, ctx.model, auth);
116
-
117
- const chain = ctx.hookRegistry.getChain(ctx.model.qualifiedName);
118
- if (!chain) {
119
- const record = await ctx.ops.create(body, auth);
120
- return reply.status(201).send({ data: record });
121
- }
122
-
123
- try {
124
- const result = await executeHookPipeline(
125
- buildPipelineOptions(ctx, 'create', body as HookDocument, auth, async (doc, trx) => {
126
- const txOps = ctx.ops.withTransaction!(trx);
127
- return txOps.create(doc) as Promise<HookDocument>;
128
- }),
129
- );
130
- return reply.status(201).send({ data: result });
131
- } catch (err: unknown) {
132
- return handleHookError(err);
133
- }
134
- };
135
- }
136
-
137
- export function withHooksUpdate(ctx: WithHooksContext) {
138
- return async (request: FastifyRequest, reply: FastifyReply) => {
139
- const { id } = request.params as { id: string };
140
- const body = parseRequestBody(request);
141
-
142
- await findRecordOrNotFound(ctx, id);
143
-
144
- const fieldViolations = validateFields(ctx.model, body, 'update');
145
- if (fieldViolations.length > 0) {
146
- throw new BadRequestError(
147
- 'VALIDATION_ERROR',
148
- fieldViolations.map((v) => v.message).join('; '),
149
- fieldViolations,
150
- );
151
- }
152
-
153
- const authContext = getAuthContext(request);
154
- const existing = await findRecordOrNotFound(ctx, id);
155
- assertOwnership(authContext.permissions, ctx.model, existing, authContext.user?.id, 'write');
156
-
157
- stampUpdate(body, ctx.model, authContext);
158
-
159
- const chain = ctx.hookRegistry.getChain(ctx.model.qualifiedName);
160
- if (!chain) {
161
- const record = await ctx.ops.update(id, body, authContext);
162
- return reply.send({ data: record });
163
- }
164
-
165
- try {
166
- const result = await executeHookPipeline(
167
- buildPipelineOptions(
168
- ctx,
169
- 'update',
170
- { ...body, id } as HookDocument,
171
- authContext,
172
- async (doc, trx) => {
173
- const { id: docId, ...data } = doc;
174
- const txOps = ctx.ops.withTransaction!(trx);
175
- return txOps.update(docId as string, data) as Promise<HookDocument>;
176
- },
177
- ),
178
- );
179
- return reply.send({ data: result });
180
- } catch (err: unknown) {
181
- return handleHookError(err);
182
- }
183
- };
184
- }
185
-
186
- export function withHooksDelete(ctx: WithHooksContext) {
187
- return async (request: FastifyRequest, reply: FastifyReply) => {
188
- const { id } = request.params as { id: string };
189
-
190
- const existing = await findRecordOrNotFound(ctx, id);
191
-
192
- const authContext = getAuthContext(request);
193
- assertOwnership(authContext.permissions, ctx.model, existing, authContext.user?.id, 'delete');
194
-
195
- const chain = ctx.hookRegistry.getChain(ctx.model.qualifiedName);
196
- if (!chain) {
197
- await ctx.ops.delete(id, authContext);
198
- return reply.status(204).send();
199
- }
200
-
201
- try {
202
- await executeHookPipeline(
203
- buildPipelineOptions(
204
- ctx,
205
- 'delete',
206
- existing as HookDocument,
207
- authContext,
208
- async (doc, trx) => {
209
- const txOps = ctx.ops.withTransaction!(trx);
210
- await txOps.delete(doc.id as string);
211
- return doc;
212
- },
213
- ),
214
- );
215
- return reply.status(204).send();
216
- } catch (err: unknown) {
217
- return handleHookError(err);
218
- }
219
- };
220
- }
@@ -1,20 +0,0 @@
1
- import type { HooksConfig } from '@rangka/shared';
2
- import type { HookChain } from './types.js';
3
-
4
- export class HookRegistry {
5
- private chains: Map<string, HookChain> = new Map();
6
-
7
- register(model: string, hooks: HooksConfig, source: string): void {
8
- const chain = this.chains.get(model) ?? { entries: [] };
9
- chain.entries.push({ hooks, source });
10
- this.chains.set(model, chain);
11
- }
12
-
13
- getChain(model: string): HookChain | undefined {
14
- return this.chains.get(model);
15
- }
16
-
17
- hasHooks(model: string): boolean {
18
- return this.chains.has(model);
19
- }
20
- }
@@ -1,32 +0,0 @@
1
- import type {
2
- HooksConfig,
3
- HookDocument,
4
- ValidateHook,
5
- BeforeHook,
6
- AfterHook,
7
- FrameworkContext,
8
- } from '@rangka/shared';
9
-
10
- export type HookLifecycle =
11
- | 'validate'
12
- | 'beforeSave'
13
- | 'afterSave'
14
- | 'beforeCreate'
15
- | 'afterCreate'
16
- | 'beforeUpdate'
17
- | 'afterUpdate'
18
- | 'beforeDelete'
19
- | 'afterDelete';
20
-
21
- export interface HookEntry {
22
- hooks: HooksConfig;
23
- source: string;
24
- }
25
-
26
- export interface HookChain {
27
- entries: HookEntry[];
28
- }
29
-
30
- export type HookContext = FrameworkContext;
31
-
32
- export type { HooksConfig, HookDocument, ValidateHook, BeforeHook, AfterHook };
package/src/index.ts DELETED
@@ -1,172 +0,0 @@
1
- export { boot } from './boot/index.js';
2
- export type { BootOptions, BootResult } from './boot/index.js';
3
- export { SchemaRegistry } from './schema/registry.js';
4
- export { loadSchemas } from './boot/schema-loader.js';
5
- export type { SchemaLoadResult } from './boot/schema-loader.js';
6
- export { mergeSchemas } from './boot/schema-merger.js';
7
- export type { MergeResult } from './boot/schema-merger.js';
8
- export type {
9
- ResolvedModel,
10
- ResolvedField,
11
- FieldProvenance,
12
- ModelRelationship,
13
- ExtensionSource,
14
- } from './schema/types.js';
15
- export type { RangkaPackageInfo, DiscoverySource, DiscoveredApp } from './boot/types.js';
16
- export {
17
- SchemaConflictError,
18
- MissingDependencyError,
19
- CircularDependencyError,
20
- } from './boot/types.js';
21
- export {
22
- AppError,
23
- BadRequestError,
24
- UnauthorizedError,
25
- ForbiddenError,
26
- NotFoundError,
27
- } from './errors.js';
28
- export { NodeModulesDiscoverySource, MemoryDiscoverySource } from './boot/discovery.js';
29
- export { ProjectScanner } from './boot/project-scanner.js';
30
- export type { ProjectScanResult } from './boot/project-scanner.js';
31
- export { DatabaseClient } from './db/client.js';
32
- export type { DatabaseClientConfig } from './db/client.js';
33
- export type {
34
- ColumnDefinition,
35
- TableDefinition,
36
- IndexDefinition,
37
- ForeignKeyDefinition,
38
- DesiredState,
39
- ActualState,
40
- DdlOperation,
41
- } from './db/types.js';
42
- export { mapFieldsToColumns, modelToTableName } from './db/field-mapper.js';
43
- export { SchemaToDesired } from './db/desired-state.js';
44
- export { DiffEngine } from './db/diff-engine.js';
45
- export { introspect } from './db/introspect.js';
46
- export { autoSync } from './db/auto-sync.js';
47
- export type { DdlOperation as CoreDdlOperation } from './db/types.js';
48
- export { createServer } from './api/server.js';
49
- export { QueryParser, QueryValidationError } from './api/query-parser.js';
50
- export { generateRoutes } from './api/route-generator.js';
51
- export type { ServerConfig, ApiDefinition } from './api/types.js';
52
- export { defineApi, defineRoles, defineConfig } from '@rangka/shared';
53
- export type { RangkaConfig } from '@rangka/shared';
54
-
55
- // Auth & Permissions
56
- export {
57
- PermissionRegistry,
58
- DuplicateRoleError,
59
- RoleInheritanceCycleError,
60
- } from './auth/permission-registry.js';
61
- export {
62
- createAuthHook,
63
- getAuthContext,
64
- createSessionHandler,
65
- deleteSessionHandler,
66
- regenerateSessionToken,
67
- generateToken,
68
- } from './auth/session.js';
69
- export { createModelPermissionGuard } from './auth/model-permissions.js';
70
- export { createScopeHook, createScopeWriteGuard, applyScopeFiltersToQuery } from './auth/scopes.js';
71
- export { ScopeRegistry, ScopeResolutionError } from './auth/scope-registry.js';
72
- export type { ResolvedScope, ModelScopeBinding } from './auth/scope-registry.js';
73
- export {
74
- createFieldWriteGuard,
75
- createFieldStripHook,
76
- resolveFieldPermissions,
77
- } from './auth/field-permissions.js';
78
- export { debugPermissions, formatDebugResult } from './auth/debug.js';
79
- export { getCoreModels, getCoreApp } from './auth/core-module.js';
80
- export { coreSchemas } from './auth/core-models.js';
81
- export { seedCoreData } from './auth/seed.js';
82
- export { hashPassword, verifyPassword } from './auth/password.js';
83
- export type {
84
- AuthUser,
85
- AuthSession,
86
- RequestContext,
87
- ScopeFilter,
88
- ResolvedPermissions,
89
- RegisteredRole,
90
- } from './auth/types.js';
91
-
92
- // Hooks
93
- export { HookRegistry } from './hooks/registry.js';
94
- export { ValidationError } from './hooks/errors.js';
95
- export { executeHookPipeline } from './hooks/executor.js';
96
- export { createHookContext } from './hooks/context.js';
97
- export type {
98
- HookChain,
99
- HookEntry,
100
- HookLifecycle,
101
- HookContext,
102
- HookDocument,
103
- } from './hooks/types.js';
104
-
105
- // Validation
106
- export { validateFields } from './validation/field-validator.js';
107
- export type { FieldViolation } from './validation/field-validator.js';
108
-
109
- // Jobs & Queue
110
- export { JobRegistry } from './jobs/registry.js';
111
- export { enqueue } from './jobs/enqueue.js';
112
- export { JobWorker } from './jobs/worker.js';
113
- export { ScheduleManager } from './jobs/scheduler.js';
114
- export { getJobTables } from './jobs/tables.js';
115
- export type {
116
- JobRecord,
117
- DeadLetterRecord,
118
- ScheduledJobRecord,
119
- JobState,
120
- BackoffStrategy,
121
- JobWorkerConfig,
122
- RegisteredJob,
123
- EnqueueOptions,
124
- } from './jobs/types.js';
125
-
126
- // Events
127
- export { EventBus } from './events/bus.js';
128
- export type { EventListener, EventBusConfig } from './events/types.js';
129
-
130
- // Services
131
- export {
132
- ServiceRegistry,
133
- ServiceCircularDependencyError,
134
- ServiceNotFoundError,
135
- DuplicateServiceError,
136
- } from './services/registry.js';
137
- export type {
138
- ServiceDefinition,
139
- ServiceFactory,
140
- ServiceInstance,
141
- ServiceDependency,
142
- ServiceContext,
143
- } from './services/types.js';
144
-
145
- // Context
146
- export { createFrameworkContext, createRequestContext } from './context.js';
147
- export type { FrameworkContextOptions, RequestScopedContextOptions } from './context.js';
148
-
149
- // Fixtures
150
- export { FixtureRegistry } from './fixtures/registry.js';
151
- export { loadFixtures } from './fixtures/loader.js';
152
- export type {
153
- FixtureDefinition,
154
- FixtureRef,
155
- FixtureRecord,
156
- FixtureStatus,
157
- FixtureLoadResult,
158
- RegisteredFixture,
159
- } from './fixtures/types.js';
160
-
161
- // Audit Log
162
- export { getAuditTables } from './audit/tables.js';
163
- export { recordAudit, getAuditHistory } from './audit/record.js';
164
- export type { AuditAction, AuditChange, AuditLogRecord, AuditOptions } from './audit/types.js';
165
-
166
- // Widgets
167
- export { WidgetRegistry } from './widgets/widget-registry.js';
168
- export { validatePageBody } from './widgets/slot-validator.js';
169
- export type { SlotValidationError } from './widgets/slot-validator.js';
170
-
171
- // Coercion helpers
172
- export { toBool, toInt, isNil, toCount } from './helpers/coerce.js';