@momentumcms/core 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +43 -34
- package/schematics/collection.json +10 -0
- package/schematics/types/index.cjs +53 -0
- package/schematics/types/index.js +28 -0
- package/schematics/types/schema.d.ts +5 -0
- package/schematics/types/schema.json +26 -0
- package/{generators → src/generators}/generator.cjs +58 -3
- package/src/generators/generator.d.ts +10 -0
- package/{generators → src/generators}/generator.js +58 -3
- package/{index.cjs → src/index.cjs} +51 -2
- package/src/index.d.ts +1 -0
- package/{index.js → src/index.js} +47 -2
- package/src/lib/collections/collection.types.d.ts +26 -2
- package/src/lib/collections/define-collection.d.ts +19 -0
- package/src/lib/collections/index.d.ts +2 -2
- package/src/lib/config.d.ts +41 -0
- package/src/lib/migrations.d.ts +93 -0
- package/src/lib/plugins.d.ts +8 -2
- package/CHANGELOG.md +0 -117
- package/LICENSE +0 -21
- package/README.md +0 -11
package/package.json
CHANGED
|
@@ -1,35 +1,44 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
2
|
+
"name": "@momentumcms/core",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Core collection config, fields, hooks, and access control for Momentum CMS",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Momentum CMS Contributors",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/DonaldMurillo/momentum-cms.git",
|
|
10
|
+
"directory": "libs/core"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/DonaldMurillo/momentum-cms#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/DonaldMurillo/momentum-cms/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"cms",
|
|
18
|
+
"headless-cms",
|
|
19
|
+
"angular",
|
|
20
|
+
"momentum-cms",
|
|
21
|
+
"collections",
|
|
22
|
+
"fields",
|
|
23
|
+
"content-management"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"main": "./src/index.cjs",
|
|
29
|
+
"module": "./src/index.js",
|
|
30
|
+
"types": "./src/index.d.ts",
|
|
31
|
+
"schematics": "./schematics/collection.json",
|
|
32
|
+
"bin": {
|
|
33
|
+
"momentum-generate": "./src/generators/generator.cjs"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@angular-devkit/schematics": ">=17.0.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"@angular-devkit/schematics": {
|
|
40
|
+
"optional": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {}
|
|
44
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json",
|
|
3
|
+
"schematics": {
|
|
4
|
+
"types": {
|
|
5
|
+
"description": "Generate TypeScript types and browser-safe admin config from momentum.config.ts",
|
|
6
|
+
"factory": "./types/index#generateTypes",
|
|
7
|
+
"schema": "./types/schema.json"
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// libs/core/schematics/types/index.ts
|
|
21
|
+
var types_exports = {};
|
|
22
|
+
__export(types_exports, {
|
|
23
|
+
generateTypes: () => generateTypes
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(types_exports);
|
|
26
|
+
var import_node_child_process = require("node:child_process");
|
|
27
|
+
function generateTypes(options) {
|
|
28
|
+
return (_tree, context) => {
|
|
29
|
+
const configPath = options.configPath || "src/momentum.config.ts";
|
|
30
|
+
const typesOutput = options.typesOutput || "src/generated/momentum.types.ts";
|
|
31
|
+
const configOutput = options.configOutput || "src/generated/momentum.config.ts";
|
|
32
|
+
context.logger.info("Generating Momentum CMS types and admin config...");
|
|
33
|
+
context.logger.info(` Config: ${configPath}`);
|
|
34
|
+
context.logger.info(` Types: ${typesOutput}`);
|
|
35
|
+
context.logger.info(` Admin: ${configOutput}`);
|
|
36
|
+
try {
|
|
37
|
+
(0, import_node_child_process.execFileSync)(
|
|
38
|
+
"npx",
|
|
39
|
+
["momentum-generate", configPath, "--types", typesOutput, "--config", configOutput],
|
|
40
|
+
{ stdio: "inherit", shell: true }
|
|
41
|
+
);
|
|
42
|
+
context.logger.info("Types and admin config generated successfully.");
|
|
43
|
+
} catch (error) {
|
|
44
|
+
context.logger.error("Failed to generate types. Is @momentumcms/core installed?");
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
return _tree;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
51
|
+
0 && (module.exports = {
|
|
52
|
+
generateTypes
|
|
53
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// libs/core/schematics/types/index.ts
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
function generateTypes(options) {
|
|
4
|
+
return (_tree, context) => {
|
|
5
|
+
const configPath = options.configPath || "src/momentum.config.ts";
|
|
6
|
+
const typesOutput = options.typesOutput || "src/generated/momentum.types.ts";
|
|
7
|
+
const configOutput = options.configOutput || "src/generated/momentum.config.ts";
|
|
8
|
+
context.logger.info("Generating Momentum CMS types and admin config...");
|
|
9
|
+
context.logger.info(` Config: ${configPath}`);
|
|
10
|
+
context.logger.info(` Types: ${typesOutput}`);
|
|
11
|
+
context.logger.info(` Admin: ${configOutput}`);
|
|
12
|
+
try {
|
|
13
|
+
execFileSync(
|
|
14
|
+
"npx",
|
|
15
|
+
["momentum-generate", configPath, "--types", typesOutput, "--config", configOutput],
|
|
16
|
+
{ stdio: "inherit", shell: true }
|
|
17
|
+
);
|
|
18
|
+
context.logger.info("Types and admin config generated successfully.");
|
|
19
|
+
} catch (error) {
|
|
20
|
+
context.logger.error("Failed to generate types. Is @momentumcms/core installed?");
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
return _tree;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
generateTypes
|
|
28
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema",
|
|
3
|
+
"$id": "MomentumGenerateTypes",
|
|
4
|
+
"title": "Momentum CMS Type Generation",
|
|
5
|
+
"description": "Generate TypeScript types and browser-safe admin config from momentum.config.ts",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"configPath": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Path to momentum.config.ts",
|
|
11
|
+
"default": "src/momentum.config.ts",
|
|
12
|
+
"x-prompt": "Path to your momentum.config.ts file?"
|
|
13
|
+
},
|
|
14
|
+
"typesOutput": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Output path for generated TypeScript types",
|
|
17
|
+
"default": "src/generated/momentum.types.ts"
|
|
18
|
+
},
|
|
19
|
+
"configOutput": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Output path for generated browser-safe admin config",
|
|
22
|
+
"default": "src/generated/momentum.config.ts"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"required": []
|
|
26
|
+
}
|
|
@@ -392,6 +392,44 @@ var FIELD_ADMIN_STRIP_KEYS = /* @__PURE__ */ new Set(["condition"]);
|
|
|
392
392
|
function isRecord(value) {
|
|
393
393
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
394
394
|
}
|
|
395
|
+
function collectFieldNames(fields) {
|
|
396
|
+
const names = [];
|
|
397
|
+
for (const field of fields) {
|
|
398
|
+
if (field.type === "tabs" && field.tabs) {
|
|
399
|
+
for (const tab of field.tabs) {
|
|
400
|
+
if (tab.name) {
|
|
401
|
+
names.push(tab.name);
|
|
402
|
+
} else {
|
|
403
|
+
names.push(...collectFieldNames(tab.fields));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
} else if ((field.type === "collapsible" || field.type === "row") && field.fields) {
|
|
407
|
+
names.push(...collectFieldNames(field.fields));
|
|
408
|
+
} else {
|
|
409
|
+
names.push(field.name);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return names;
|
|
413
|
+
}
|
|
414
|
+
function previewFunctionToTemplate(fn, fields) {
|
|
415
|
+
try {
|
|
416
|
+
const sentinel = "__MCMS_FIELD_";
|
|
417
|
+
const mockDoc = {};
|
|
418
|
+
for (const name of collectFieldNames(fields)) {
|
|
419
|
+
mockDoc[name] = `${sentinel}${name}__`;
|
|
420
|
+
}
|
|
421
|
+
const result = fn(mockDoc);
|
|
422
|
+
if (typeof result !== "string")
|
|
423
|
+
return true;
|
|
424
|
+
const template = result.replace(
|
|
425
|
+
new RegExp(`${sentinel}(\\w+)__`, "g"),
|
|
426
|
+
(_match, fieldName) => `{${fieldName}}`
|
|
427
|
+
);
|
|
428
|
+
return template;
|
|
429
|
+
} catch {
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
395
433
|
function serializeValue(value, indent = " ") {
|
|
396
434
|
if (value === null)
|
|
397
435
|
return "null";
|
|
@@ -584,9 +622,13 @@ function serializeCollection(collection, indent = " ") {
|
|
|
584
622
|
}
|
|
585
623
|
parts.push(`${indent} fields: ${serializeFieldsArray(collection.fields, indent + " ")}`);
|
|
586
624
|
if (collection.admin) {
|
|
587
|
-
const adminEntries = Object.entries(collection.admin).filter(
|
|
588
|
-
(
|
|
589
|
-
|
|
625
|
+
const adminEntries = Object.entries(collection.admin).filter(([, v]) => v !== void 0).map(([k, v]) => {
|
|
626
|
+
if (k === "preview" && typeof v === "function") {
|
|
627
|
+
const fn = v;
|
|
628
|
+
return [k, previewFunctionToTemplate(fn, collection.fields)];
|
|
629
|
+
}
|
|
630
|
+
return [k, v];
|
|
631
|
+
}).filter(([, v]) => typeof v !== "function");
|
|
590
632
|
if (adminEntries.length > 0) {
|
|
591
633
|
const adminObj = Object.fromEntries(adminEntries);
|
|
592
634
|
parts.push(`${indent} admin: ${serializeValue(adminObj, indent + " ")}`);
|
|
@@ -610,6 +652,9 @@ function serializeCollection(collection, indent = " ") {
|
|
|
610
652
|
if (collection.defaultSort) {
|
|
611
653
|
parts.push(`${indent} defaultSort: ${JSON.stringify(collection.defaultSort)}`);
|
|
612
654
|
}
|
|
655
|
+
if (collection.upload !== void 0) {
|
|
656
|
+
parts.push(`${indent} upload: ${serializeValue(collection.upload, indent + " ")}`);
|
|
657
|
+
}
|
|
613
658
|
return `{
|
|
614
659
|
${parts.join(",\n")},
|
|
615
660
|
${indent}}`;
|
|
@@ -739,6 +784,15 @@ function parseArgs(args) {
|
|
|
739
784
|
}
|
|
740
785
|
return { configPath, typesOutputPath, configOutputPath, watch: watchMode };
|
|
741
786
|
}
|
|
787
|
+
function formatWithPrettier(...filePaths) {
|
|
788
|
+
try {
|
|
789
|
+
(0, import_node_child_process.execFileSync)("npx", ["prettier", "--write", ...filePaths], {
|
|
790
|
+
stdio: "pipe"
|
|
791
|
+
});
|
|
792
|
+
} catch {
|
|
793
|
+
console.warn("prettier not available \u2014 skipping formatting of generated files");
|
|
794
|
+
}
|
|
795
|
+
}
|
|
742
796
|
async function runGenerator(options) {
|
|
743
797
|
const configPath = (0, import_node_path.resolve)(options.configPath);
|
|
744
798
|
const typesOutputPath = (0, import_node_path.resolve)(options.typesOutputPath);
|
|
@@ -758,6 +812,7 @@ async function runGenerator(options) {
|
|
|
758
812
|
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(configOutputPath), { recursive: true });
|
|
759
813
|
(0, import_node_fs.writeFileSync)(configOutputPath, adminConfigContent, "utf-8");
|
|
760
814
|
console.info(`Admin config generated: ${configOutputPath}`);
|
|
815
|
+
formatWithPrettier(typesOutputPath, configOutputPath);
|
|
761
816
|
} catch (error) {
|
|
762
817
|
console.error(`Error generating:`, error);
|
|
763
818
|
throw error;
|
|
@@ -91,6 +91,16 @@ interface CollectionDefinition {
|
|
|
91
91
|
defaultWhere?: unknown;
|
|
92
92
|
endpoints?: unknown[];
|
|
93
93
|
webhooks?: unknown[];
|
|
94
|
+
upload?: {
|
|
95
|
+
mimeTypes?: string[];
|
|
96
|
+
maxFileSize?: number;
|
|
97
|
+
directory?: string;
|
|
98
|
+
filenameField?: string;
|
|
99
|
+
mimeTypeField?: string;
|
|
100
|
+
filesizeField?: string;
|
|
101
|
+
pathField?: string;
|
|
102
|
+
urlField?: string;
|
|
103
|
+
};
|
|
94
104
|
}
|
|
95
105
|
interface GlobalDefinition {
|
|
96
106
|
slug: string;
|
|
@@ -361,6 +361,44 @@ var FIELD_ADMIN_STRIP_KEYS = /* @__PURE__ */ new Set(["condition"]);
|
|
|
361
361
|
function isRecord(value) {
|
|
362
362
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
363
363
|
}
|
|
364
|
+
function collectFieldNames(fields) {
|
|
365
|
+
const names = [];
|
|
366
|
+
for (const field of fields) {
|
|
367
|
+
if (field.type === "tabs" && field.tabs) {
|
|
368
|
+
for (const tab of field.tabs) {
|
|
369
|
+
if (tab.name) {
|
|
370
|
+
names.push(tab.name);
|
|
371
|
+
} else {
|
|
372
|
+
names.push(...collectFieldNames(tab.fields));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
} else if ((field.type === "collapsible" || field.type === "row") && field.fields) {
|
|
376
|
+
names.push(...collectFieldNames(field.fields));
|
|
377
|
+
} else {
|
|
378
|
+
names.push(field.name);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return names;
|
|
382
|
+
}
|
|
383
|
+
function previewFunctionToTemplate(fn, fields) {
|
|
384
|
+
try {
|
|
385
|
+
const sentinel = "__MCMS_FIELD_";
|
|
386
|
+
const mockDoc = {};
|
|
387
|
+
for (const name of collectFieldNames(fields)) {
|
|
388
|
+
mockDoc[name] = `${sentinel}${name}__`;
|
|
389
|
+
}
|
|
390
|
+
const result = fn(mockDoc);
|
|
391
|
+
if (typeof result !== "string")
|
|
392
|
+
return true;
|
|
393
|
+
const template = result.replace(
|
|
394
|
+
new RegExp(`${sentinel}(\\w+)__`, "g"),
|
|
395
|
+
(_match, fieldName) => `{${fieldName}}`
|
|
396
|
+
);
|
|
397
|
+
return template;
|
|
398
|
+
} catch {
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
364
402
|
function serializeValue(value, indent = " ") {
|
|
365
403
|
if (value === null)
|
|
366
404
|
return "null";
|
|
@@ -553,9 +591,13 @@ function serializeCollection(collection, indent = " ") {
|
|
|
553
591
|
}
|
|
554
592
|
parts.push(`${indent} fields: ${serializeFieldsArray(collection.fields, indent + " ")}`);
|
|
555
593
|
if (collection.admin) {
|
|
556
|
-
const adminEntries = Object.entries(collection.admin).filter(
|
|
557
|
-
(
|
|
558
|
-
|
|
594
|
+
const adminEntries = Object.entries(collection.admin).filter(([, v]) => v !== void 0).map(([k, v]) => {
|
|
595
|
+
if (k === "preview" && typeof v === "function") {
|
|
596
|
+
const fn = v;
|
|
597
|
+
return [k, previewFunctionToTemplate(fn, collection.fields)];
|
|
598
|
+
}
|
|
599
|
+
return [k, v];
|
|
600
|
+
}).filter(([, v]) => typeof v !== "function");
|
|
559
601
|
if (adminEntries.length > 0) {
|
|
560
602
|
const adminObj = Object.fromEntries(adminEntries);
|
|
561
603
|
parts.push(`${indent} admin: ${serializeValue(adminObj, indent + " ")}`);
|
|
@@ -579,6 +621,9 @@ function serializeCollection(collection, indent = " ") {
|
|
|
579
621
|
if (collection.defaultSort) {
|
|
580
622
|
parts.push(`${indent} defaultSort: ${JSON.stringify(collection.defaultSort)}`);
|
|
581
623
|
}
|
|
624
|
+
if (collection.upload !== void 0) {
|
|
625
|
+
parts.push(`${indent} upload: ${serializeValue(collection.upload, indent + " ")}`);
|
|
626
|
+
}
|
|
582
627
|
return `{
|
|
583
628
|
${parts.join(",\n")},
|
|
584
629
|
${indent}}`;
|
|
@@ -708,6 +753,15 @@ function parseArgs(args) {
|
|
|
708
753
|
}
|
|
709
754
|
return { configPath, typesOutputPath, configOutputPath, watch: watchMode };
|
|
710
755
|
}
|
|
756
|
+
function formatWithPrettier(...filePaths) {
|
|
757
|
+
try {
|
|
758
|
+
execFileSync("npx", ["prettier", "--write", ...filePaths], {
|
|
759
|
+
stdio: "pipe"
|
|
760
|
+
});
|
|
761
|
+
} catch {
|
|
762
|
+
console.warn("prettier not available \u2014 skipping formatting of generated files");
|
|
763
|
+
}
|
|
764
|
+
}
|
|
711
765
|
async function runGenerator(options) {
|
|
712
766
|
const configPath = resolve(options.configPath);
|
|
713
767
|
const typesOutputPath = resolve(options.typesOutputPath);
|
|
@@ -727,6 +781,7 @@ async function runGenerator(options) {
|
|
|
727
781
|
mkdirSync(dirname(configOutputPath), { recursive: true });
|
|
728
782
|
writeFileSync(configOutputPath, adminConfigContent, "utf-8");
|
|
729
783
|
console.info(`Admin config generated: ${configOutputPath}`);
|
|
784
|
+
formatWithPrettier(typesOutputPath, configOutputPath);
|
|
730
785
|
} catch (error) {
|
|
731
786
|
console.error(`Error generating:`, error);
|
|
732
787
|
throw error;
|
|
@@ -46,6 +46,7 @@ __export(src_exports, {
|
|
|
46
46
|
getDbAdapter: () => getDbAdapter,
|
|
47
47
|
getGlobals: () => getGlobals,
|
|
48
48
|
getSoftDeleteField: () => getSoftDeleteField,
|
|
49
|
+
getUploadFieldMapping: () => getUploadFieldMapping,
|
|
49
50
|
group: () => group,
|
|
50
51
|
hasAllRoles: () => hasAllRoles,
|
|
51
52
|
hasAnyRole: () => hasAnyRole,
|
|
@@ -55,6 +56,7 @@ __export(src_exports, {
|
|
|
55
56
|
isLayoutField: () => isLayoutField,
|
|
56
57
|
isNamedTab: () => isNamedTab,
|
|
57
58
|
isOwner: () => isOwner,
|
|
59
|
+
isUploadCollection: () => isUploadCollection,
|
|
58
60
|
json: () => json,
|
|
59
61
|
not: () => not,
|
|
60
62
|
number: () => number,
|
|
@@ -63,6 +65,8 @@ __export(src_exports, {
|
|
|
63
65
|
point: () => point,
|
|
64
66
|
radio: () => radio,
|
|
65
67
|
relationship: () => relationship,
|
|
68
|
+
resolveMigrationConfig: () => resolveMigrationConfig,
|
|
69
|
+
resolveMigrationMode: () => resolveMigrationMode,
|
|
66
70
|
richText: () => richText,
|
|
67
71
|
row: () => row,
|
|
68
72
|
select: () => select,
|
|
@@ -117,6 +121,21 @@ function getSoftDeleteField(config) {
|
|
|
117
121
|
const sdConfig = config.softDelete;
|
|
118
122
|
return sdConfig.field ?? "deletedAt";
|
|
119
123
|
}
|
|
124
|
+
function isUploadCollection(config) {
|
|
125
|
+
return config.upload != null;
|
|
126
|
+
}
|
|
127
|
+
function getUploadFieldMapping(config) {
|
|
128
|
+
if (!isUploadCollection(config))
|
|
129
|
+
return null;
|
|
130
|
+
const u = config.upload;
|
|
131
|
+
return {
|
|
132
|
+
filename: u.filenameField ?? "filename",
|
|
133
|
+
mimeType: u.mimeTypeField ?? "mimeType",
|
|
134
|
+
filesize: u.filesizeField ?? "filesize",
|
|
135
|
+
path: u.pathField ?? "path",
|
|
136
|
+
url: u.urlField ?? "url"
|
|
137
|
+
};
|
|
138
|
+
}
|
|
120
139
|
|
|
121
140
|
// libs/core/src/lib/fields/field.types.ts
|
|
122
141
|
var LAYOUT_FIELD_TYPES = /* @__PURE__ */ new Set(["tabs", "collapsible", "row"]);
|
|
@@ -430,6 +449,9 @@ var MediaCollection = defineCollection({
|
|
|
430
449
|
singular: "Media",
|
|
431
450
|
plural: "Media"
|
|
432
451
|
},
|
|
452
|
+
upload: {
|
|
453
|
+
mimeTypes: ["image/*", "application/pdf", "video/*", "audio/*"]
|
|
454
|
+
},
|
|
433
455
|
admin: {
|
|
434
456
|
useAsTitle: "filename",
|
|
435
457
|
defaultColumns: ["filename", "mimeType", "filesize", "createdAt"]
|
|
@@ -450,7 +472,6 @@ var MediaCollection = defineCollection({
|
|
|
450
472
|
description: "File size in bytes"
|
|
451
473
|
}),
|
|
452
474
|
text("path", {
|
|
453
|
-
required: true,
|
|
454
475
|
label: "Storage Path",
|
|
455
476
|
description: "Path/key where the file is stored",
|
|
456
477
|
admin: {
|
|
@@ -561,6 +582,29 @@ function isOwner(ownerField = "createdBy") {
|
|
|
561
582
|
};
|
|
562
583
|
}
|
|
563
584
|
|
|
585
|
+
// libs/core/src/lib/migrations.ts
|
|
586
|
+
function resolveMigrationMode(mode) {
|
|
587
|
+
if (mode === "push" || mode === "migrate")
|
|
588
|
+
return mode;
|
|
589
|
+
const env = process.env["NODE_ENV"];
|
|
590
|
+
if (env === "production")
|
|
591
|
+
return "migrate";
|
|
592
|
+
return "push";
|
|
593
|
+
}
|
|
594
|
+
function resolveMigrationConfig(config) {
|
|
595
|
+
if (!config)
|
|
596
|
+
return void 0;
|
|
597
|
+
const mode = resolveMigrationMode(config.mode);
|
|
598
|
+
return {
|
|
599
|
+
...config,
|
|
600
|
+
directory: config.directory ?? "./migrations",
|
|
601
|
+
mode,
|
|
602
|
+
cloneTest: config.cloneTest ?? mode === "migrate",
|
|
603
|
+
dangerDetection: config.dangerDetection ?? true,
|
|
604
|
+
autoApply: config.autoApply ?? mode === "push"
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
|
|
564
608
|
// libs/core/src/lib/config.ts
|
|
565
609
|
var MIN_PASSWORD_LENGTH = 8;
|
|
566
610
|
function defineMomentumConfig(config) {
|
|
@@ -591,7 +635,8 @@ function defineMomentumConfig(config) {
|
|
|
591
635
|
level: config.logging?.level ?? "info",
|
|
592
636
|
format: config.logging?.format ?? "pretty",
|
|
593
637
|
timestamps: config.logging?.timestamps ?? true
|
|
594
|
-
}
|
|
638
|
+
},
|
|
639
|
+
migrations: resolveMigrationConfig(config.migrations)
|
|
595
640
|
};
|
|
596
641
|
}
|
|
597
642
|
function getDbAdapter(config) {
|
|
@@ -717,6 +762,7 @@ function createSeedHelpers() {
|
|
|
717
762
|
getDbAdapter,
|
|
718
763
|
getGlobals,
|
|
719
764
|
getSoftDeleteField,
|
|
765
|
+
getUploadFieldMapping,
|
|
720
766
|
group,
|
|
721
767
|
hasAllRoles,
|
|
722
768
|
hasAnyRole,
|
|
@@ -726,6 +772,7 @@ function createSeedHelpers() {
|
|
|
726
772
|
isLayoutField,
|
|
727
773
|
isNamedTab,
|
|
728
774
|
isOwner,
|
|
775
|
+
isUploadCollection,
|
|
729
776
|
json,
|
|
730
777
|
not,
|
|
731
778
|
number,
|
|
@@ -734,6 +781,8 @@ function createSeedHelpers() {
|
|
|
734
781
|
point,
|
|
735
782
|
radio,
|
|
736
783
|
relationship,
|
|
784
|
+
resolveMigrationConfig,
|
|
785
|
+
resolveMigrationMode,
|
|
737
786
|
richText,
|
|
738
787
|
row,
|
|
739
788
|
select,
|
package/src/index.d.ts
CHANGED
|
@@ -40,6 +40,21 @@ function getSoftDeleteField(config) {
|
|
|
40
40
|
const sdConfig = config.softDelete;
|
|
41
41
|
return sdConfig.field ?? "deletedAt";
|
|
42
42
|
}
|
|
43
|
+
function isUploadCollection(config) {
|
|
44
|
+
return config.upload != null;
|
|
45
|
+
}
|
|
46
|
+
function getUploadFieldMapping(config) {
|
|
47
|
+
if (!isUploadCollection(config))
|
|
48
|
+
return null;
|
|
49
|
+
const u = config.upload;
|
|
50
|
+
return {
|
|
51
|
+
filename: u.filenameField ?? "filename",
|
|
52
|
+
mimeType: u.mimeTypeField ?? "mimeType",
|
|
53
|
+
filesize: u.filesizeField ?? "filesize",
|
|
54
|
+
path: u.pathField ?? "path",
|
|
55
|
+
url: u.urlField ?? "url"
|
|
56
|
+
};
|
|
57
|
+
}
|
|
43
58
|
|
|
44
59
|
// libs/core/src/lib/fields/field.types.ts
|
|
45
60
|
var LAYOUT_FIELD_TYPES = /* @__PURE__ */ new Set(["tabs", "collapsible", "row"]);
|
|
@@ -353,6 +368,9 @@ var MediaCollection = defineCollection({
|
|
|
353
368
|
singular: "Media",
|
|
354
369
|
plural: "Media"
|
|
355
370
|
},
|
|
371
|
+
upload: {
|
|
372
|
+
mimeTypes: ["image/*", "application/pdf", "video/*", "audio/*"]
|
|
373
|
+
},
|
|
356
374
|
admin: {
|
|
357
375
|
useAsTitle: "filename",
|
|
358
376
|
defaultColumns: ["filename", "mimeType", "filesize", "createdAt"]
|
|
@@ -373,7 +391,6 @@ var MediaCollection = defineCollection({
|
|
|
373
391
|
description: "File size in bytes"
|
|
374
392
|
}),
|
|
375
393
|
text("path", {
|
|
376
|
-
required: true,
|
|
377
394
|
label: "Storage Path",
|
|
378
395
|
description: "Path/key where the file is stored",
|
|
379
396
|
admin: {
|
|
@@ -484,6 +501,29 @@ function isOwner(ownerField = "createdBy") {
|
|
|
484
501
|
};
|
|
485
502
|
}
|
|
486
503
|
|
|
504
|
+
// libs/core/src/lib/migrations.ts
|
|
505
|
+
function resolveMigrationMode(mode) {
|
|
506
|
+
if (mode === "push" || mode === "migrate")
|
|
507
|
+
return mode;
|
|
508
|
+
const env = process.env["NODE_ENV"];
|
|
509
|
+
if (env === "production")
|
|
510
|
+
return "migrate";
|
|
511
|
+
return "push";
|
|
512
|
+
}
|
|
513
|
+
function resolveMigrationConfig(config) {
|
|
514
|
+
if (!config)
|
|
515
|
+
return void 0;
|
|
516
|
+
const mode = resolveMigrationMode(config.mode);
|
|
517
|
+
return {
|
|
518
|
+
...config,
|
|
519
|
+
directory: config.directory ?? "./migrations",
|
|
520
|
+
mode,
|
|
521
|
+
cloneTest: config.cloneTest ?? mode === "migrate",
|
|
522
|
+
dangerDetection: config.dangerDetection ?? true,
|
|
523
|
+
autoApply: config.autoApply ?? mode === "push"
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
|
|
487
527
|
// libs/core/src/lib/config.ts
|
|
488
528
|
var MIN_PASSWORD_LENGTH = 8;
|
|
489
529
|
function defineMomentumConfig(config) {
|
|
@@ -514,7 +554,8 @@ function defineMomentumConfig(config) {
|
|
|
514
554
|
level: config.logging?.level ?? "info",
|
|
515
555
|
format: config.logging?.format ?? "pretty",
|
|
516
556
|
timestamps: config.logging?.timestamps ?? true
|
|
517
|
-
}
|
|
557
|
+
},
|
|
558
|
+
migrations: resolveMigrationConfig(config.migrations)
|
|
518
559
|
};
|
|
519
560
|
}
|
|
520
561
|
function getDbAdapter(config) {
|
|
@@ -639,6 +680,7 @@ export {
|
|
|
639
680
|
getDbAdapter,
|
|
640
681
|
getGlobals,
|
|
641
682
|
getSoftDeleteField,
|
|
683
|
+
getUploadFieldMapping,
|
|
642
684
|
group,
|
|
643
685
|
hasAllRoles,
|
|
644
686
|
hasAnyRole,
|
|
@@ -648,6 +690,7 @@ export {
|
|
|
648
690
|
isLayoutField,
|
|
649
691
|
isNamedTab,
|
|
650
692
|
isOwner,
|
|
693
|
+
isUploadCollection,
|
|
651
694
|
json,
|
|
652
695
|
not,
|
|
653
696
|
number,
|
|
@@ -656,6 +699,8 @@ export {
|
|
|
656
699
|
point,
|
|
657
700
|
radio,
|
|
658
701
|
relationship,
|
|
702
|
+
resolveMigrationConfig,
|
|
703
|
+
resolveMigrationMode,
|
|
659
704
|
richText,
|
|
660
705
|
row,
|
|
661
706
|
select,
|
|
@@ -74,8 +74,8 @@ export interface AdminConfig {
|
|
|
74
74
|
description?: string;
|
|
75
75
|
/** Hide from admin navigation */
|
|
76
76
|
hidden?: boolean;
|
|
77
|
-
/** Enable preview mode */
|
|
78
|
-
preview?: boolean | ((doc: Record<string, unknown>) => string);
|
|
77
|
+
/** Enable preview mode. String values are URL templates with {fieldName} placeholders. */
|
|
78
|
+
preview?: boolean | string | ((doc: Record<string, unknown>) => string);
|
|
79
79
|
/** Custom action buttons displayed in the collection list header (alongside Create button) */
|
|
80
80
|
headerActions?: Array<{
|
|
81
81
|
id: string;
|
|
@@ -120,6 +120,24 @@ export interface SoftDeleteConfig {
|
|
|
120
120
|
/** Auto-purge soft-deleted records after this many days. Undefined means never purge. */
|
|
121
121
|
retentionDays?: number;
|
|
122
122
|
}
|
|
123
|
+
export interface UploadCollectionConfig {
|
|
124
|
+
/** Allowed MIME types for uploads to this collection (e.g., ['image/*', 'application/pdf']) */
|
|
125
|
+
mimeTypes?: string[];
|
|
126
|
+
/** Maximum file size in bytes (overrides global storage.maxFileSize) */
|
|
127
|
+
maxFileSize?: number;
|
|
128
|
+
/** Subdirectory within the upload dir for this collection's files */
|
|
129
|
+
directory?: string;
|
|
130
|
+
/** Field name for the original filename. @default 'filename' */
|
|
131
|
+
filenameField?: string;
|
|
132
|
+
/** Field name for the MIME type. @default 'mimeType' */
|
|
133
|
+
mimeTypeField?: string;
|
|
134
|
+
/** Field name for the file size in bytes. @default 'filesize' */
|
|
135
|
+
filesizeField?: string;
|
|
136
|
+
/** Field name for the storage path. @default 'path' */
|
|
137
|
+
pathField?: string;
|
|
138
|
+
/** Field name for the public URL. @default 'url' */
|
|
139
|
+
urlField?: string;
|
|
140
|
+
}
|
|
123
141
|
export interface TimestampsConfig {
|
|
124
142
|
/** Add createdAt field */
|
|
125
143
|
createdAt?: boolean;
|
|
@@ -188,6 +206,12 @@ export interface CollectionConfig {
|
|
|
188
206
|
endpoints?: EndpointConfig[];
|
|
189
207
|
/** Webhook subscriptions for this collection */
|
|
190
208
|
webhooks?: WebhookConfig[];
|
|
209
|
+
/**
|
|
210
|
+
* Upload configuration. When present, this collection becomes an "upload collection"
|
|
211
|
+
* where POST /api/{slug} accepts multipart/form-data and auto-populates file metadata fields.
|
|
212
|
+
* Similar to Payload CMS's upload collection pattern.
|
|
213
|
+
*/
|
|
214
|
+
upload?: UploadCollectionConfig;
|
|
191
215
|
}
|
|
192
216
|
/** Events that trigger webhooks. */
|
|
193
217
|
export type WebhookEvent = 'afterChange' | 'afterDelete' | 'afterCreate' | 'afterUpdate';
|
|
@@ -40,6 +40,25 @@ export declare function defineGlobal(config: GlobalConfig): GlobalConfig;
|
|
|
40
40
|
* @returns The deletedAt field name, or null if soft delete is not enabled
|
|
41
41
|
*/
|
|
42
42
|
export declare function getSoftDeleteField(config: CollectionConfig): string | null;
|
|
43
|
+
/**
|
|
44
|
+
* Check whether a collection is configured as an upload collection.
|
|
45
|
+
*/
|
|
46
|
+
export declare function isUploadCollection(config: CollectionConfig): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Resolved field name mapping for upload collection metadata fields.
|
|
49
|
+
*/
|
|
50
|
+
export interface UploadFieldMapping {
|
|
51
|
+
filename: string;
|
|
52
|
+
mimeType: string;
|
|
53
|
+
filesize: string;
|
|
54
|
+
path: string;
|
|
55
|
+
url: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get the resolved upload field mapping for an upload collection.
|
|
59
|
+
* Returns field names used for auto-populating file metadata, or null for non-upload collections.
|
|
60
|
+
*/
|
|
61
|
+
export declare function getUploadFieldMapping(config: CollectionConfig): UploadFieldMapping | null;
|
|
43
62
|
/**
|
|
44
63
|
* Helper type to extract the document type from a collection
|
|
45
64
|
* Useful for typing API responses and database queries
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export * from './collection.types';
|
|
2
|
-
export { defineCollection, defineGlobal, getSoftDeleteField } from './define-collection';
|
|
3
|
-
export type { InferDocumentType } from './define-collection';
|
|
2
|
+
export { defineCollection, defineGlobal, getSoftDeleteField, isUploadCollection, getUploadFieldMapping, } from './define-collection';
|
|
3
|
+
export type { InferDocumentType, UploadFieldMapping } from './define-collection';
|
|
4
4
|
export { MediaCollection } from './media.collection';
|
|
5
5
|
export type { MediaDocument } from './media.collection';
|
package/src/lib/config.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { SeedingConfig, SeedingOptions } from './seeding';
|
|
|
3
3
|
import type { DocumentVersion, DocumentStatus, VersionQueryOptions, VersionCountOptions, CreateVersionOptions } from './versions';
|
|
4
4
|
import type { MomentumPlugin, PluginAdminRouteDescriptor } from './plugins';
|
|
5
5
|
import type { StorageAdapter } from './storage';
|
|
6
|
+
import type { MigrationConfig, ResolvedMigrationConfig } from './migrations';
|
|
6
7
|
/**
|
|
7
8
|
* Minimum password length for user accounts.
|
|
8
9
|
* Shared across seeding, user sync hooks, and setup middleware.
|
|
@@ -13,6 +14,12 @@ export declare const MIN_PASSWORD_LENGTH = 8;
|
|
|
13
14
|
* This is a placeholder type - actual adapters implement this interface.
|
|
14
15
|
*/
|
|
15
16
|
export interface DatabaseAdapter {
|
|
17
|
+
/**
|
|
18
|
+
* Database dialect identifier.
|
|
19
|
+
* Set by the adapter factory (e.g., postgresAdapter sets 'postgresql', sqliteAdapter sets 'sqlite').
|
|
20
|
+
* Used by the migration CLI to select the correct introspection and SQL generation strategy.
|
|
21
|
+
*/
|
|
22
|
+
dialect?: 'postgresql' | 'sqlite';
|
|
16
23
|
find(collection: string, query: Record<string, unknown>): Promise<Record<string, unknown>[]>;
|
|
17
24
|
findById(collection: string, id: string): Promise<Record<string, unknown> | null>;
|
|
18
25
|
create(collection: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
@@ -148,6 +155,34 @@ export interface DatabaseAdapter {
|
|
|
148
155
|
* @returns The full global record after update
|
|
149
156
|
*/
|
|
150
157
|
updateGlobal?(slug: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
158
|
+
/**
|
|
159
|
+
* Introspect the current database schema.
|
|
160
|
+
* Returns table/column metadata for diffing against collection config.
|
|
161
|
+
*/
|
|
162
|
+
introspect?(): Promise<Record<string, unknown>>;
|
|
163
|
+
/**
|
|
164
|
+
* Execute a raw SQL statement (DDL or DML).
|
|
165
|
+
* Returns the number of affected rows.
|
|
166
|
+
*/
|
|
167
|
+
executeRaw?(sql: string, params?: unknown[]): Promise<number>;
|
|
168
|
+
/**
|
|
169
|
+
* Execute a raw SQL query and return rows.
|
|
170
|
+
*/
|
|
171
|
+
queryRaw?<T extends Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
172
|
+
/**
|
|
173
|
+
* Clone the database for migration testing.
|
|
174
|
+
* Returns a connection string or path for the clone.
|
|
175
|
+
*/
|
|
176
|
+
cloneDatabase?(targetName: string): Promise<string>;
|
|
177
|
+
/**
|
|
178
|
+
* Drop a cloned database.
|
|
179
|
+
*/
|
|
180
|
+
dropClone?(targetName: string): Promise<void>;
|
|
181
|
+
/**
|
|
182
|
+
* Acquire an advisory lock for migration safety.
|
|
183
|
+
* Returns a release function.
|
|
184
|
+
*/
|
|
185
|
+
acquireMigrationLock?(): Promise<() => Promise<void>>;
|
|
151
186
|
}
|
|
152
187
|
/**
|
|
153
188
|
* Database configuration options.
|
|
@@ -327,6 +362,11 @@ export interface MomentumConfig {
|
|
|
327
362
|
* Plugins run in array order during init/ready, reverse during shutdown.
|
|
328
363
|
*/
|
|
329
364
|
plugins?: MomentumPlugin[];
|
|
365
|
+
/**
|
|
366
|
+
* Migration system configuration.
|
|
367
|
+
* When set, enables the schema migration system.
|
|
368
|
+
*/
|
|
369
|
+
migrations?: MigrationConfig;
|
|
330
370
|
}
|
|
331
371
|
/**
|
|
332
372
|
* Resolved seeding options with defaults applied.
|
|
@@ -346,6 +386,7 @@ export interface ResolvedMomentumConfig extends MomentumConfig {
|
|
|
346
386
|
server: Required<ServerConfig>;
|
|
347
387
|
seeding?: ResolvedSeedingConfig;
|
|
348
388
|
logging: ResolvedLoggingConfig;
|
|
389
|
+
migrations?: ResolvedMigrationConfig;
|
|
349
390
|
}
|
|
350
391
|
/**
|
|
351
392
|
* Defines Momentum CMS configuration.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration Types for Momentum CMS
|
|
3
|
+
*
|
|
4
|
+
* Universal (browser + server) types for the migration system.
|
|
5
|
+
* Server-only implementation lives in @momentumcms/migrations.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Configuration for the migration system.
|
|
9
|
+
* Added to MomentumConfig.migrations when migrations are enabled.
|
|
10
|
+
*/
|
|
11
|
+
export interface MigrationConfig {
|
|
12
|
+
/**
|
|
13
|
+
* Directory where migration files are stored.
|
|
14
|
+
* Relative to the app root.
|
|
15
|
+
* @default './migrations'
|
|
16
|
+
*/
|
|
17
|
+
directory?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Database operation mode.
|
|
20
|
+
* - 'push': Dev mode — direct schema sync, no migration files
|
|
21
|
+
* - 'migrate': Production mode — migration files required
|
|
22
|
+
* - 'auto': 'push' in development, 'migrate' in production
|
|
23
|
+
* @default 'auto'
|
|
24
|
+
*/
|
|
25
|
+
mode?: 'push' | 'migrate' | 'auto';
|
|
26
|
+
/**
|
|
27
|
+
* Enable clone-test-apply safety pipeline before applying migrations.
|
|
28
|
+
* When enabled, migrations are first tested on a database clone.
|
|
29
|
+
* @default true in migrate mode
|
|
30
|
+
*/
|
|
31
|
+
cloneTest?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Enable dangerous operation detection and warnings.
|
|
34
|
+
* @default true
|
|
35
|
+
*/
|
|
36
|
+
dangerDetection?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Automatically apply pending migrations on server start.
|
|
39
|
+
* Only applies in 'push' mode. Migrate mode always requires explicit CLI commands.
|
|
40
|
+
* @default true in push mode
|
|
41
|
+
*/
|
|
42
|
+
autoApply?: boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Status of an individual applied migration.
|
|
46
|
+
*/
|
|
47
|
+
export interface MigrationStatus {
|
|
48
|
+
/** Migration filename (without extension) */
|
|
49
|
+
name: string;
|
|
50
|
+
/** Batch number (migrations applied together share a batch) */
|
|
51
|
+
batch: number;
|
|
52
|
+
/** When the migration was applied (ISO string) */
|
|
53
|
+
appliedAt: string;
|
|
54
|
+
/** SHA-256 checksum of the migration file */
|
|
55
|
+
checksum: string;
|
|
56
|
+
/** How long the migration took to execute (ms) */
|
|
57
|
+
executionMs: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Overall migration system status (browser-safe for admin UI).
|
|
61
|
+
*/
|
|
62
|
+
export interface MigrationSystemStatus {
|
|
63
|
+
/** Whether the database schema matches the collection config */
|
|
64
|
+
inSync: boolean;
|
|
65
|
+
/** Number of pending (unapplied) migrations */
|
|
66
|
+
pending: number;
|
|
67
|
+
/** List of applied migrations */
|
|
68
|
+
applied: MigrationStatus[];
|
|
69
|
+
/** Current migration mode */
|
|
70
|
+
mode: 'push' | 'migrate';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Severity levels for dangerous operation warnings.
|
|
74
|
+
*/
|
|
75
|
+
export type DangerSeverity = 'warning' | 'destructive' | 'irreversible';
|
|
76
|
+
/**
|
|
77
|
+
* Resolved migration config with defaults applied.
|
|
78
|
+
*/
|
|
79
|
+
export interface ResolvedMigrationConfig extends MigrationConfig {
|
|
80
|
+
directory: string;
|
|
81
|
+
mode: 'push' | 'migrate';
|
|
82
|
+
cloneTest: boolean;
|
|
83
|
+
dangerDetection: boolean;
|
|
84
|
+
autoApply: boolean;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Resolve the effective migration mode from config and environment.
|
|
88
|
+
*/
|
|
89
|
+
export declare function resolveMigrationMode(mode: MigrationConfig['mode']): 'push' | 'migrate';
|
|
90
|
+
/**
|
|
91
|
+
* Resolve migration config with defaults applied.
|
|
92
|
+
*/
|
|
93
|
+
export declare function resolveMigrationConfig(config: MigrationConfig | undefined): ResolvedMigrationConfig | undefined;
|
package/src/lib/plugins.d.ts
CHANGED
|
@@ -17,8 +17,14 @@ export interface PluginMiddlewareDescriptor {
|
|
|
17
17
|
path: string;
|
|
18
18
|
/** Express Router or middleware function. Typed as unknown to avoid Express dependency in core. */
|
|
19
19
|
handler: unknown;
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Where to mount relative to collection CRUD routes.
|
|
22
|
+
* - 'before-api': Before collection CRUD routes (under /api)
|
|
23
|
+
* - 'after-api': After collection CRUD routes (under /api)
|
|
24
|
+
* - 'root': Mounted at the application root (not under /api)
|
|
25
|
+
* @default 'before-api'
|
|
26
|
+
*/
|
|
27
|
+
position?: 'before-api' | 'after-api' | 'root';
|
|
22
28
|
}
|
|
23
29
|
/**
|
|
24
30
|
* Descriptor for Angular DI providers that a plugin wants auto-registered during SSR.
|
package/CHANGELOG.md
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
## 0.2.0 (2026-02-17)
|
|
2
|
-
|
|
3
|
-
### 🚀 Features
|
|
4
|
-
|
|
5
|
-
- add named tabs support with nested data grouping and tab UI improvements ([63ab63e](https://github.com/DonaldMurillo/momentum-cms/commit/63ab63e))
|
|
6
|
-
|
|
7
|
-
### ❤️ Thank You
|
|
8
|
-
|
|
9
|
-
- Claude Opus 4.6
|
|
10
|
-
- Donald Murillo @DonaldMurillo
|
|
11
|
-
|
|
12
|
-
## 0.1.10 (2026-02-17)
|
|
13
|
-
|
|
14
|
-
### 🩹 Fixes
|
|
15
|
-
|
|
16
|
-
- **create-momentum-app:** add shell option to execFileSync for Windows ([#28](https://github.com/DonaldMurillo/momentum-cms/pull/28))
|
|
17
|
-
|
|
18
|
-
### ❤️ Thank You
|
|
19
|
-
|
|
20
|
-
- Claude Opus 4.6
|
|
21
|
-
- Donald Murillo @DonaldMurillo
|
|
22
|
-
|
|
23
|
-
## 0.1.9 (2026-02-16)
|
|
24
|
-
|
|
25
|
-
This was a version bump only for core to align it with other projects, there were no code changes.
|
|
26
|
-
|
|
27
|
-
## 0.1.8 (2026-02-16)
|
|
28
|
-
|
|
29
|
-
### 🩹 Fixes
|
|
30
|
-
|
|
31
|
-
- correct repository URLs and add GitHub link to CLI ([#26](https://github.com/DonaldMurillo/momentum-cms/pull/26))
|
|
32
|
-
|
|
33
|
-
### ❤️ Thank You
|
|
34
|
-
|
|
35
|
-
- Claude Opus 4.6
|
|
36
|
-
- Donald Murillo @DonaldMurillo
|
|
37
|
-
|
|
38
|
-
## 0.1.7 (2026-02-16)
|
|
39
|
-
|
|
40
|
-
### 🩹 Fixes
|
|
41
|
-
|
|
42
|
-
- correct repository URLs and add GitHub link to CLI output ([f7e96bb](https://github.com/DonaldMurillo/momentum-cms/commit/f7e96bb))
|
|
43
|
-
|
|
44
|
-
### ❤️ Thank You
|
|
45
|
-
|
|
46
|
-
- Claude Opus 4.6
|
|
47
|
-
- Donald Murillo @DonaldMurillo
|
|
48
|
-
|
|
49
|
-
## 0.1.6 (2026-02-16)
|
|
50
|
-
|
|
51
|
-
This was a version bump only for core to align it with other projects, there were no code changes.
|
|
52
|
-
|
|
53
|
-
## 0.1.5 (2026-02-16)
|
|
54
|
-
|
|
55
|
-
### 🚀 Features
|
|
56
|
-
|
|
57
|
-
- **create-app:** add landing page, fix setup flow, theme detection, type generator, Playwright E2E ([5e0f4ed](https://github.com/DonaldMurillo/momentum-cms/commit/5e0f4ed))
|
|
58
|
-
|
|
59
|
-
### ❤️ Thank You
|
|
60
|
-
|
|
61
|
-
- Claude Opus 4.6
|
|
62
|
-
- Donald Murillo @DonaldMurillo
|
|
63
|
-
|
|
64
|
-
## 0.1.4 (2026-02-16)
|
|
65
|
-
|
|
66
|
-
This was a version bump only for core to align it with other projects, there were no code changes.
|
|
67
|
-
|
|
68
|
-
## 0.1.3 (2026-02-16)
|
|
69
|
-
|
|
70
|
-
This was a version bump only for core to align it with other projects, there were no code changes.
|
|
71
|
-
|
|
72
|
-
## 0.1.2 (2026-02-16)
|
|
73
|
-
|
|
74
|
-
### 🩹 Fixes
|
|
75
|
-
|
|
76
|
-
- **release:** centralize manifestRootsToUpdate to update both source and dist ([2b8f832](https://github.com/DonaldMurillo/momentum-cms/commit/2b8f832))
|
|
77
|
-
- **create-app:** fix Angular SSR, Analog builds, and CJS/ESM compatibility ([28d4d0a](https://github.com/DonaldMurillo/momentum-cms/commit/28d4d0a))
|
|
78
|
-
|
|
79
|
-
### ❤️ Thank You
|
|
80
|
-
|
|
81
|
-
- Claude Opus 4.6
|
|
82
|
-
- Donald Murillo @DonaldMurillo
|
|
83
|
-
|
|
84
|
-
## 0.1.1 (2026-02-16)
|
|
85
|
-
|
|
86
|
-
This was a version bump only for core to align it with other projects, there were no code changes.
|
|
87
|
-
|
|
88
|
-
## 0.1.0 (2026-02-16)
|
|
89
|
-
|
|
90
|
-
### 🚀 Features
|
|
91
|
-
|
|
92
|
-
- implement soft deletes with full stack support ([#22](https://github.com/DonaldMurillo/momentum-cms/pull/22))
|
|
93
|
-
- add tracking rules, content performance, and block analytics ([#21](https://github.com/DonaldMurillo/momentum-cms/pull/21))
|
|
94
|
-
- implement globals (singleton collections) with full stack support ([#20](https://github.com/DonaldMurillo/momentum-cms/pull/20))
|
|
95
|
-
- visual block editor & auth-gated admin mode ([#18](https://github.com/DonaldMurillo/momentum-cms/pull/18))
|
|
96
|
-
- Add display formatting and complex field rendering ([#14](https://github.com/DonaldMurillo/momentum-cms/pull/14))
|
|
97
|
-
- UI polish fixes and database-level FK constraints for relationship integrity ([#13](https://github.com/DonaldMurillo/momentum-cms/pull/13))
|
|
98
|
-
- Add document versioning and drafts system ([#5](https://github.com/DonaldMurillo/momentum-cms/pull/5))
|
|
99
|
-
- Add seeding feature with idempotent data initialization ([#1](https://github.com/DonaldMurillo/momentum-cms/pull/1))
|
|
100
|
-
- Add typed access control helper functions ([980d8d0](https://github.com/DonaldMurillo/momentum-cms/commit/980d8d0))
|
|
101
|
-
- Add type-safe Momentum API with signal support ([aee6c02](https://github.com/DonaldMurillo/momentum-cms/commit/aee6c02))
|
|
102
|
-
- Implement admin UI with API integration and SSR hydration ([9ed7b2b](https://github.com/DonaldMurillo/momentum-cms/commit/9ed7b2b))
|
|
103
|
-
- Initialize Momentum CMS foundation ([f64f581](https://github.com/DonaldMurillo/momentum-cms/commit/f64f581))
|
|
104
|
-
|
|
105
|
-
### 🩹 Fixes
|
|
106
|
-
|
|
107
|
-
- resolve CUD toast interceptor issues ([#17](https://github.com/DonaldMurillo/momentum-cms/pull/17), [#1](https://github.com/DonaldMurillo/momentum-cms/issues/1), [#2](https://github.com/DonaldMurillo/momentum-cms/issues/2), [#3](https://github.com/DonaldMurillo/momentum-cms/issues/3), [#4](https://github.com/DonaldMurillo/momentum-cms/issues/4))
|
|
108
|
-
- address 7 critical and high-severity security and validation bugs ([#12](https://github.com/DonaldMurillo/momentum-cms/pull/12))
|
|
109
|
-
- address security vulnerabilities from code review ([#9](https://github.com/DonaldMurillo/momentum-cms/pull/9))
|
|
110
|
-
- address security and reliability issues from code review ([#7](https://github.com/DonaldMurillo/momentum-cms/pull/7))
|
|
111
|
-
|
|
112
|
-
### ❤️ Thank You
|
|
113
|
-
|
|
114
|
-
- Claude Haiku 4.5
|
|
115
|
-
- Claude Opus 4.5
|
|
116
|
-
- Claude Opus 4.6
|
|
117
|
-
- Donald Murillo @DonaldMurillo
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2024-present Momentum CMS Contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/README.md
DELETED