@momentumcms/server-core 0.0.1

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 ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@momentumcms/server-core",
3
+ "version": "0.0.1",
4
+ "description": "Framework-agnostic server handlers for Momentum CMS",
5
+ "license": "MIT",
6
+ "author": "Momentum CMS Contributors",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/momentum-cms/momentum-cms.git",
10
+ "directory": "libs/server-core"
11
+ },
12
+ "homepage": "https://github.com/momentum-cms/momentum-cms#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/momentum-cms/momentum-cms/issues"
15
+ },
16
+ "keywords": [
17
+ "cms",
18
+ "momentum-cms",
19
+ "server",
20
+ "api",
21
+ "rest",
22
+ "crud"
23
+ ],
24
+ "engines": {
25
+ "node": ">=18"
26
+ },
27
+ "type": "commonjs",
28
+ "main": "./index.cjs",
29
+ "types": "./src/index.d.ts",
30
+ "peerDependencies": {
31
+ "@momentumcms/core": ">=0.0.1",
32
+ "@momentumcms/logger": ">=0.0.1",
33
+ "@momentumcms/storage": ">=0.0.1",
34
+ "graphql": "^16.0.0"
35
+ },
36
+ "dependencies": {
37
+ "@aws-sdk/client-s3": "^3.983.0",
38
+ "@aws-sdk/s3-request-presigner": "^3.983.0"
39
+ }
40
+ }
package/src/index.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ export { createMomentumHandlers, createInMemoryAdapter, type DatabaseAdapter, type MomentumConfig, type ResolvedMomentumConfig, type QueryOptions, type MomentumRequest, type MomentumResponse, type MomentumHandlers, type ValidationError, } from './lib/server-core';
2
+ export { initializeMomentumAPI, getMomentumAPI, isMomentumAPIInitialized, resetMomentumAPI, CollectionNotFoundError, DocumentNotFoundError, AccessDeniedError, GlobalNotFoundError, ReferentialIntegrityError, ValidationError as MomentumValidationError, type MomentumAPI, type MomentumAPIContext, type CollectionOperations, type GlobalOperations, type FindOptions, type FindResult, type DeleteResult, type WhereClause, type FieldValidationError, type VersionOperations, type VersionFindOptions, } from './lib/momentum-api';
3
+ export { VersionOperationsImpl } from './lib/version-operations';
4
+ export { hasFieldAccessControl, filterReadableFields, filterCreatableFields, filterUpdatableFields, } from './lib/field-access';
5
+ export { hasFieldHooks, runFieldHooks } from './lib/field-hooks';
6
+ export { populateRelationships, type PopulateOptions } from './lib/relationship-populator';
7
+ export { checkCollectionAdminAccess, checkSingleCollectionAdminAccess, getCollectionPermissions, type CollectionAccess, type CollectionPermissions, type AccessResponse, } from './lib/collection-access';
8
+ export { runSeeding, shouldRunSeeding, calculateChecksum, createSeedTracker, type MomentumAuthLike, type SeedingResult, type SeedingRunOptions, type SeedTracker, type CreateSeedTrackingData, } from './lib/seeding';
9
+ export { registerWebhookHooks } from './lib/webhooks';
10
+ export { startPublishScheduler, type PublishSchedulerOptions, type PublishSchedulerHandle, } from './lib/publish-scheduler';
11
+ export { buildGraphQLSchema, type GraphQLContext } from './lib/graphql-schema';
12
+ export { executeGraphQL, type GraphQLRequestBody, type GraphQLResult } from './lib/graphql-handler';
13
+ export { GraphQLJSON } from './lib/graphql-scalars';
14
+ export { generateApiKey, hashApiKey, getKeyPrefix, isValidApiKeyFormat, generateApiKeyId, createAdapterApiKeyStore, createPostgresApiKeyStore, API_KEYS_TABLE_SQL_POSTGRES, API_KEYS_TABLE_SQL_SQLITE, type ApiKeyRecord, type CreateApiKeyResult, type CreateApiKeyOptions, type ApiKeyStore, } from './lib/api-keys';
15
+ export { handleUpload, handleFileDelete, handleFileGet, getUploadConfig, type UploadRequest, type UploadResponse, type UploadConfig, } from './lib/upload-handler';
16
+ export { generateOpenAPISpec, type OpenAPIDocument, type OpenAPIGeneratorOptions, } from './lib/openapi-generator';
17
+ export { getSwaggerUIHTML } from './lib/swagger-ui-html';
18
+ export { renderPreviewHTML, type PreviewRenderOptions } from './lib/preview-renderer';
19
+ export { RateLimiter } from './lib/rate-limiter';
20
+ export { sanitizeErrorMessage, parseWhereParam, sanitizeFilename } from './lib/shared-server-utils';
21
+ export { exportToJson, exportToCsv, parseJsonImport, parseCsvImport, type ExportFormat, type ExportOptions, type ExportResult, type ImportOptions, type ImportResult, type ImportError, } from './lib/import-export';
@@ -0,0 +1,105 @@
1
+ import type { DatabaseAdapter } from '@momentumcms/core';
2
+ /**
3
+ * Stored API key record.
4
+ */
5
+ export interface ApiKeyRecord {
6
+ id: string;
7
+ name: string;
8
+ /** SHA-256 hash of the full key */
9
+ keyHash: string;
10
+ /** First 8 chars of the key for display (e.g., "mcms_abc1...") */
11
+ keyPrefix: string;
12
+ /** User ID that created this key */
13
+ createdBy: string;
14
+ /** Role assigned to this key for access control */
15
+ role: string;
16
+ expiresAt: string | null;
17
+ lastUsedAt: string | null;
18
+ createdAt: string;
19
+ updatedAt: string;
20
+ }
21
+ /**
22
+ * Result of creating a new API key.
23
+ * The full key is only returned once at creation time.
24
+ */
25
+ export interface CreateApiKeyResult {
26
+ id: string;
27
+ name: string;
28
+ /** The full API key - only shown once */
29
+ key: string;
30
+ keyPrefix: string;
31
+ role: string;
32
+ expiresAt: string | null;
33
+ createdAt: string;
34
+ }
35
+ /**
36
+ * Options for creating an API key.
37
+ */
38
+ export interface CreateApiKeyOptions {
39
+ name: string;
40
+ role?: string;
41
+ expiresAt?: Date;
42
+ }
43
+ /**
44
+ * Generate a cryptographically secure API key.
45
+ * Format: mcms_ + 40 hex chars = 45 chars total
46
+ */
47
+ export declare function generateApiKey(): string;
48
+ /**
49
+ * Hash an API key using SHA-256 for secure storage.
50
+ */
51
+ export declare function hashApiKey(key: string): string;
52
+ /**
53
+ * Extract a display-safe prefix from an API key.
54
+ * Returns the first 12 chars (prefix + 7 hex chars).
55
+ */
56
+ export declare function getKeyPrefix(key: string): string;
57
+ /**
58
+ * Validate that a string looks like a Momentum API key.
59
+ */
60
+ export declare function isValidApiKeyFormat(key: string): boolean;
61
+ /**
62
+ * Generate a unique ID for an API key record.
63
+ */
64
+ export declare function generateApiKeyId(): string;
65
+ /**
66
+ * Database operations for API keys.
67
+ * Uses raw SQL queries through the database adapter.
68
+ */
69
+ export interface ApiKeyStore {
70
+ /** Create a new API key record, returns the created record ID */
71
+ create(record: Omit<ApiKeyRecord, 'lastUsedAt'>): Promise<string>;
72
+ /** Find an API key by its hash */
73
+ findByHash(keyHash: string): Promise<ApiKeyRecord | null>;
74
+ /** List all API keys (without sensitive data) */
75
+ listAll(): Promise<Omit<ApiKeyRecord, 'keyHash'>[]>;
76
+ /** List API keys created by a specific user */
77
+ listByUser(userId: string): Promise<Omit<ApiKeyRecord, 'keyHash'>[]>;
78
+ /** Find an API key by ID (without keyHash) */
79
+ findById(id: string): Promise<Omit<ApiKeyRecord, 'keyHash'> | null>;
80
+ /** Delete an API key by ID */
81
+ deleteById(id: string): Promise<boolean>;
82
+ /** Update last used timestamp */
83
+ updateLastUsed(id: string, timestamp: string): Promise<void>;
84
+ }
85
+ /**
86
+ * SQL for creating the API keys table (PostgreSQL).
87
+ */
88
+ export declare const API_KEYS_TABLE_SQL_POSTGRES = "\n\tCREATE TABLE IF NOT EXISTS \"_api_keys\" (\n\t\t\"id\" VARCHAR(36) PRIMARY KEY NOT NULL,\n\t\t\"name\" VARCHAR(255) NOT NULL,\n\t\t\"keyHash\" VARCHAR(64) NOT NULL UNIQUE,\n\t\t\"keyPrefix\" VARCHAR(20) NOT NULL,\n\t\t\"createdBy\" VARCHAR(36) NOT NULL REFERENCES \"user\"(\"id\") ON DELETE CASCADE,\n\t\t\"role\" VARCHAR(50) NOT NULL DEFAULT 'user',\n\t\t\"expiresAt\" TIMESTAMPTZ,\n\t\t\"lastUsedAt\" TIMESTAMPTZ,\n\t\t\"createdAt\" TIMESTAMPTZ NOT NULL,\n\t\t\"updatedAt\" TIMESTAMPTZ NOT NULL\n\t);\n\n\tCREATE INDEX IF NOT EXISTS \"idx_api_keys_keyHash\" ON \"_api_keys\"(\"keyHash\");\n\tCREATE INDEX IF NOT EXISTS \"idx_api_keys_createdBy\" ON \"_api_keys\"(\"createdBy\");\n";
89
+ /**
90
+ * SQL for creating the API keys table (SQLite).
91
+ */
92
+ export declare const API_KEYS_TABLE_SQL_SQLITE = "\n\tCREATE TABLE IF NOT EXISTS \"_api_keys\" (\n\t\t\"id\" TEXT PRIMARY KEY NOT NULL,\n\t\t\"name\" TEXT NOT NULL,\n\t\t\"keyHash\" TEXT NOT NULL UNIQUE,\n\t\t\"keyPrefix\" TEXT NOT NULL,\n\t\t\"createdBy\" TEXT NOT NULL,\n\t\t\"role\" TEXT NOT NULL DEFAULT 'user',\n\t\t\"expiresAt\" TEXT,\n\t\t\"lastUsedAt\" TEXT,\n\t\t\"createdAt\" TEXT NOT NULL,\n\t\t\"updatedAt\" TEXT NOT NULL,\n\t\tFOREIGN KEY (\"createdBy\") REFERENCES \"user\"(\"id\") ON DELETE CASCADE\n\t);\n\n\tCREATE INDEX IF NOT EXISTS \"idx_api_keys_keyHash\" ON \"_api_keys\"(\"keyHash\");\n\tCREATE INDEX IF NOT EXISTS \"idx_api_keys_createdBy\" ON \"_api_keys\"(\"createdBy\");\n";
93
+ /**
94
+ * Create an API key store backed by a generic DatabaseAdapter.
95
+ * Works with any adapter (SQLite, Postgres, etc.) using collection CRUD methods.
96
+ */
97
+ export declare function createAdapterApiKeyStore(adapter: DatabaseAdapter): ApiKeyStore;
98
+ /**
99
+ * Create an API key store backed by PostgreSQL.
100
+ */
101
+ export declare function createPostgresApiKeyStore(query: {
102
+ query: (sql: string, params?: unknown[]) => Promise<any[]>;
103
+ queryOne: (sql: string, params?: unknown[]) => Promise<any | null>;
104
+ execute: (sql: string, params?: unknown[]) => Promise<number>;
105
+ }): ApiKeyStore;
@@ -0,0 +1,54 @@
1
+ import type { MomentumConfig, CollectionConfig, UserContext } from '@momentumcms/core';
2
+ /**
3
+ * Result of checking admin access for a single collection.
4
+ */
5
+ export interface CollectionAccess {
6
+ slug: string;
7
+ canAccess: boolean;
8
+ }
9
+ /**
10
+ * Full access permissions for a collection.
11
+ */
12
+ export interface CollectionPermissions {
13
+ slug: string;
14
+ canAccess: boolean;
15
+ canCreate: boolean;
16
+ canRead: boolean;
17
+ canUpdate: boolean;
18
+ canDelete: boolean;
19
+ /** True if this collection is managed (read-only via API, owned by a plugin like Better Auth). */
20
+ managed?: boolean;
21
+ }
22
+ /**
23
+ * Response shape for the /access endpoint.
24
+ */
25
+ export interface AccessResponse {
26
+ collections: CollectionPermissions[];
27
+ }
28
+ /**
29
+ * Checks if a user has admin panel access to a specific collection.
30
+ *
31
+ * @param collection - The collection config to check
32
+ * @param user - The current user context (undefined if not authenticated)
33
+ * @returns true if access is allowed, false otherwise
34
+ */
35
+ export declare function checkSingleCollectionAdminAccess(collection: CollectionConfig, user: UserContext | undefined): Promise<boolean>;
36
+ /**
37
+ * Checks admin access for all collections in a configuration.
38
+ * Returns which collections the user can access in the admin panel.
39
+ *
40
+ * @param config - The Momentum CMS configuration
41
+ * @param user - The current user context (undefined if not authenticated)
42
+ * @returns Array of collection slugs with their access status
43
+ */
44
+ export declare function checkCollectionAdminAccess(config: MomentumConfig, user: UserContext | undefined): Promise<CollectionAccess[]>;
45
+ /**
46
+ * Gets full permissions for all collections.
47
+ * Used by the /access endpoint to inform the frontend about what
48
+ * operations the user can perform.
49
+ *
50
+ * @param config - The Momentum CMS configuration
51
+ * @param user - The current user context (undefined if not authenticated)
52
+ * @returns Full permissions for each collection
53
+ */
54
+ export declare function getCollectionPermissions(config: MomentumConfig, user: UserContext | undefined): Promise<CollectionPermissions[]>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Field-Level Access Control
3
+ *
4
+ * Enforces FieldAccessConfig (create/read/update) by filtering
5
+ * fields from request data or response documents based on permissions.
6
+ */
7
+ import type { Field, RequestContext } from '@momentumcms/core';
8
+ /**
9
+ * Check if any fields in the collection have access control defined.
10
+ * Recursively checks through all field nesting (groups, arrays, blocks, layout fields).
11
+ * Used as a fast-path to skip processing when no field access is configured.
12
+ */
13
+ export declare function hasFieldAccessControl(fields: Field[]): boolean;
14
+ /**
15
+ * Filter fields the user cannot read from a response document.
16
+ * Returns a new document with restricted fields removed.
17
+ */
18
+ export declare function filterReadableFields(fields: Field[], doc: Record<string, unknown>, req: RequestContext): Promise<Record<string, unknown>>;
19
+ /**
20
+ * Filter fields the user cannot create from input data.
21
+ * Returns a new data object with restricted fields removed.
22
+ */
23
+ export declare function filterCreatableFields(fields: Field[], data: Record<string, unknown>, req: RequestContext): Promise<Record<string, unknown>>;
24
+ /**
25
+ * Filter fields the user cannot update from input data.
26
+ * Returns a new data object with restricted fields removed.
27
+ */
28
+ export declare function filterUpdatableFields(fields: Field[], data: Record<string, unknown>, req: RequestContext): Promise<Record<string, unknown>>;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Field-Level Hook Execution
3
+ *
4
+ * Runs FieldHooksConfig (beforeValidate/beforeChange/afterChange/afterRead)
5
+ * for each field that has hooks defined.
6
+ */
7
+ import type { Field, RequestContext } from '@momentumcms/core';
8
+ type FieldHookType = 'beforeValidate' | 'beforeChange' | 'afterChange' | 'afterRead';
9
+ /**
10
+ * Check if any fields in the collection have hooks defined.
11
+ * Recursively checks through all field nesting (groups, arrays, blocks, layout fields).
12
+ */
13
+ export declare function hasFieldHooks(fields: Field[]): boolean;
14
+ /**
15
+ * Run field-level hooks for a specific hook type.
16
+ * Iterates through all fields and runs matching hooks, allowing each to transform the field value.
17
+ * Recurses into groups, arrays, blocks, and layout fields.
18
+ * Returns the data with any transformed values.
19
+ */
20
+ export declare function runFieldHooks(hookType: FieldHookType, fields: Field[], data: Record<string, unknown>, req: RequestContext, operation: 'create' | 'update' | 'read'): Promise<Record<string, unknown>>;
21
+ export {};
@@ -0,0 +1,25 @@
1
+ /**
2
+ * GraphQL HTTP handler for Momentum CMS.
3
+ *
4
+ * Provides a framework-agnostic handler that executes GraphQL queries
5
+ * against the auto-generated schema. Can be used by Express, h3, etc.
6
+ */
7
+ import { type GraphQLSchema } from 'graphql';
8
+ import type { UserContext } from '@momentumcms/core';
9
+ /** Parsed GraphQL request body. */
10
+ export interface GraphQLRequestBody {
11
+ query: string;
12
+ variables?: Record<string, unknown>;
13
+ operationName?: string;
14
+ }
15
+ /** Result from executeGraphQL. */
16
+ export interface GraphQLResult {
17
+ status: number;
18
+ body: unknown;
19
+ }
20
+ /**
21
+ * Execute a GraphQL request against the provided schema.
22
+ */
23
+ export declare function executeGraphQL(schema: GraphQLSchema, requestBody: GraphQLRequestBody, context: {
24
+ user?: UserContext;
25
+ }): Promise<GraphQLResult>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Custom GraphQL scalar types for Momentum CMS.
3
+ */
4
+ import { GraphQLScalarType } from 'graphql';
5
+ /** JSON scalar - accepts and returns arbitrary JSON values. */
6
+ export declare const GraphQLJSON: GraphQLScalarType<unknown, unknown>;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * GraphQL Schema Generator for Momentum CMS.
3
+ *
4
+ * Auto-generates a GraphQL schema from Momentum collection configs,
5
+ * including query/mutation types with resolvers that delegate to the MomentumAPI.
6
+ */
7
+ import { GraphQLSchema } from 'graphql';
8
+ import type { CollectionConfig, UserContext } from '@momentumcms/core';
9
+ /** Context passed to every GraphQL resolver. */
10
+ export interface GraphQLContext {
11
+ user?: UserContext;
12
+ }
13
+ /**
14
+ * Build a full GraphQL schema from a list of collection configs.
15
+ */
16
+ export declare function buildGraphQLSchema(collections: CollectionConfig[]): GraphQLSchema;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Import/Export module for Momentum CMS.
3
+ *
4
+ * Provides JSON and CSV export/import for collection documents.
5
+ * - Export: Fetches all documents and serializes to JSON or CSV
6
+ * - Import: Parses JSON or CSV data and creates documents via batch operations
7
+ *
8
+ * No external dependencies - uses built-in CSV serialization.
9
+ */
10
+ import type { CollectionConfig } from '@momentumcms/core';
11
+ export type ExportFormat = 'json' | 'csv';
12
+ export interface ExportOptions {
13
+ /** Export format (default: 'json') */
14
+ format?: ExportFormat;
15
+ /** Max documents to export (default: unlimited) */
16
+ limit?: number;
17
+ }
18
+ export interface ExportResult {
19
+ /** Format of the exported data */
20
+ format: ExportFormat;
21
+ /** Number of documents exported */
22
+ totalDocs: number;
23
+ /** For JSON format: the array of documents */
24
+ docs?: Record<string, unknown>[];
25
+ /** For CSV format: the CSV string */
26
+ data?: string;
27
+ /** Content-Type header value */
28
+ contentType: string;
29
+ }
30
+ export interface ImportOptions {
31
+ /** Import format (default: 'json') */
32
+ format?: ExportFormat;
33
+ }
34
+ export interface ImportResult {
35
+ /** Number of successfully imported documents */
36
+ imported: number;
37
+ /** Total items attempted */
38
+ total: number;
39
+ /** Error details for failed items */
40
+ errors: ImportError[];
41
+ /** Successfully created documents */
42
+ docs: Record<string, unknown>[];
43
+ }
44
+ export interface ImportError {
45
+ /** Index of the item in the import data */
46
+ index: number;
47
+ /** Error message */
48
+ message: string;
49
+ /** The data that failed to import */
50
+ data?: Record<string, unknown>;
51
+ }
52
+ /**
53
+ * Export documents from a collection as JSON.
54
+ */
55
+ export declare function exportToJson(docs: Record<string, unknown>[], _collection: CollectionConfig): ExportResult;
56
+ /**
57
+ * Export documents from a collection as CSV.
58
+ */
59
+ export declare function exportToCsv(docs: Record<string, unknown>[], collection: CollectionConfig): ExportResult;
60
+ /**
61
+ * Parse JSON import data into document records.
62
+ * Accepts either an array of objects or `{ docs: [...] }`.
63
+ */
64
+ export declare function parseJsonImport(body: unknown): {
65
+ docs: Record<string, unknown>[];
66
+ error?: string;
67
+ };
68
+ /**
69
+ * Parse CSV import data into document records.
70
+ * First row is treated as header (field names).
71
+ */
72
+ export declare function parseCsvImport(csvData: string, collection: CollectionConfig): {
73
+ docs: Record<string, unknown>[];
74
+ error?: string;
75
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Momentum API Implementation
3
+ *
4
+ * Provides direct database access for server-side operations.
5
+ * This is the core of the Momentum API that both SSR and Express use.
6
+ */
7
+ import type { MomentumConfig } from '@momentumcms/core';
8
+ import type { MomentumAPI } from './momentum-api.types';
9
+ /**
10
+ * Initialize the Momentum API singleton.
11
+ * Must be called once at server startup before using getMomentumAPI().
12
+ *
13
+ * @param config - The Momentum configuration
14
+ * @returns The initialized API instance
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // In server.ts
19
+ * import { initializeMomentumAPI } from '@momentumcms/server-core';
20
+ * import momentumConfig from './momentum.config';
21
+ *
22
+ * initializeMomentumAPI(momentumConfig);
23
+ * ```
24
+ */
25
+ export declare function initializeMomentumAPI(config: MomentumConfig): MomentumAPI;
26
+ /**
27
+ * Get the initialized Momentum API singleton.
28
+ * Throws if not initialized.
29
+ *
30
+ * @returns The API instance
31
+ * @throws Error if not initialized
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const api = getMomentumAPI();
36
+ * const posts = await api.collection('posts').find();
37
+ * ```
38
+ */
39
+ export declare function getMomentumAPI(): MomentumAPI;
40
+ /**
41
+ * Check if the Momentum API has been initialized.
42
+ */
43
+ export declare function isMomentumAPIInitialized(): boolean;
44
+ /**
45
+ * Reset the Momentum API singleton.
46
+ * Primarily used for testing.
47
+ */
48
+ export declare function resetMomentumAPI(): void;
49
+ export type { MomentumAPI, MomentumAPIContext, CollectionOperations, GlobalOperations, FindOptions, FindResult, DeleteResult, WhereClause, FieldValidationError, VersionOperations, VersionFindOptions, } from './momentum-api.types';
50
+ export { CollectionNotFoundError, DocumentNotFoundError, AccessDeniedError, GlobalNotFoundError, ValidationError, } from './momentum-api.types';
51
+ export { ReferentialIntegrityError } from '@momentumcms/core';