@momentumcms/server-analog 0.5.3 → 0.5.4
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/CHANGELOG.md +4 -0
- package/index.cjs +74 -7
- package/index.js +74 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## 0.5.4 (2026-03-07)
|
|
2
|
+
|
|
3
|
+
This was a version bump only for server-analog to align it with other projects, there were no code changes.
|
|
4
|
+
|
|
1
5
|
## 0.5.0 (2026-02-23)
|
|
2
6
|
|
|
3
7
|
This was a version bump only for server-analog to align it with other projects, there were no code changes.
|
package/index.cjs
CHANGED
|
@@ -1452,6 +1452,14 @@ var MediaCollection = defineCollection({
|
|
|
1452
1452
|
}
|
|
1453
1453
|
});
|
|
1454
1454
|
|
|
1455
|
+
// libs/core/src/lib/versions/version.types.ts
|
|
1456
|
+
function hasVersionDrafts(collection) {
|
|
1457
|
+
const v = collection.versions;
|
|
1458
|
+
if (!v || typeof v === "boolean")
|
|
1459
|
+
return false;
|
|
1460
|
+
return !!v.drafts;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1455
1463
|
// libs/server-core/src/lib/field-access.ts
|
|
1456
1464
|
function hasFieldAccessControl(fields) {
|
|
1457
1465
|
for (const field of fields) {
|
|
@@ -2038,13 +2046,18 @@ var VersionOperationsImpl = class {
|
|
|
2038
2046
|
}
|
|
2039
2047
|
return "draft";
|
|
2040
2048
|
}
|
|
2041
|
-
async compare(versionId1, versionId2) {
|
|
2049
|
+
async compare(versionId1, versionId2, parentId) {
|
|
2042
2050
|
await this.checkAccess("readVersions");
|
|
2043
2051
|
const version1 = await this.findVersionById(versionId1);
|
|
2044
2052
|
const version2 = await this.findVersionById(versionId2);
|
|
2045
2053
|
if (!version1 || !version2) {
|
|
2046
2054
|
throw new Error("One or both versions not found");
|
|
2047
2055
|
}
|
|
2056
|
+
if (parentId) {
|
|
2057
|
+
if (version1.parent !== parentId || version2.parent !== parentId) {
|
|
2058
|
+
throw new Error("Version does not belong to the specified document");
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2048
2061
|
const data1 = version1.version;
|
|
2049
2062
|
const data2 = version2.version;
|
|
2050
2063
|
if (!isRecord(data1) || !isRecord(data2)) {
|
|
@@ -2099,6 +2112,9 @@ var VersionOperationsImpl = class {
|
|
|
2099
2112
|
// Private Helpers
|
|
2100
2113
|
// ============================================
|
|
2101
2114
|
async checkAccess(operation) {
|
|
2115
|
+
if (this.context.overrideAccess) {
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2102
2118
|
const accessFn = this.collectionConfig.access?.[operation];
|
|
2103
2119
|
if (!accessFn) {
|
|
2104
2120
|
return;
|
|
@@ -2255,6 +2271,12 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2255
2271
|
Object.assign(whereParams, constraints);
|
|
2256
2272
|
}
|
|
2257
2273
|
}
|
|
2274
|
+
if (hasVersionDrafts(this.collectionConfig) && !this.context.overrideAccess) {
|
|
2275
|
+
const canSeeDrafts = await this.canReadDrafts();
|
|
2276
|
+
if (!canSeeDrafts) {
|
|
2277
|
+
whereParams["_status"] = "published";
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2258
2280
|
const query = {
|
|
2259
2281
|
...queryOptions,
|
|
2260
2282
|
...whereParams,
|
|
@@ -2317,6 +2339,15 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2317
2339
|
doc[softDeleteField]) {
|
|
2318
2340
|
return null;
|
|
2319
2341
|
}
|
|
2342
|
+
if (hasVersionDrafts(this.collectionConfig) && !this.context.overrideAccess) {
|
|
2343
|
+
const canSeeDrafts = await this.canReadDrafts();
|
|
2344
|
+
if (!canSeeDrafts) {
|
|
2345
|
+
const record = doc;
|
|
2346
|
+
if (record["_status"] !== "published") {
|
|
2347
|
+
return null;
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2320
2351
|
if (!this.matchesDefaultWhereConstraints(doc)) {
|
|
2321
2352
|
return null;
|
|
2322
2353
|
}
|
|
@@ -2402,6 +2433,15 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2402
2433
|
if (!this.matchesDefaultWhereConstraints(originalDoc)) {
|
|
2403
2434
|
throw new DocumentNotFoundError(this.slug, id);
|
|
2404
2435
|
}
|
|
2436
|
+
if (hasVersionDrafts(this.collectionConfig) && this.adapter.createVersion) {
|
|
2437
|
+
const status = originalDoc["_status"] ?? "draft";
|
|
2438
|
+
await this.adapter.createVersion(this.slug, id, originalDoc, { status });
|
|
2439
|
+
const versionsConfig = this.collectionConfig.versions;
|
|
2440
|
+
const maxPerDoc = typeof versionsConfig === "object" && versionsConfig !== null ? versionsConfig.maxPerDoc : void 0;
|
|
2441
|
+
if (maxPerDoc && this.adapter.deleteVersions) {
|
|
2442
|
+
await this.adapter.deleteVersions(this.slug, id, maxPerDoc);
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2405
2445
|
let processedData = data;
|
|
2406
2446
|
const softDeleteField = getSoftDeleteField(this.collectionConfig);
|
|
2407
2447
|
if (softDeleteField && softDeleteField in processedData) {
|
|
@@ -2733,6 +2773,30 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2733
2773
|
throw new AccessDeniedError(operation, this.slug);
|
|
2734
2774
|
}
|
|
2735
2775
|
}
|
|
2776
|
+
/**
|
|
2777
|
+
* Check if the current user can see draft documents (non-throwing).
|
|
2778
|
+
* Uses `access.readDrafts` if configured, otherwise falls back to `access.update`.
|
|
2779
|
+
*/
|
|
2780
|
+
async canReadDrafts() {
|
|
2781
|
+
if (!this.context.user)
|
|
2782
|
+
return false;
|
|
2783
|
+
const readDraftsFn = this.collectionConfig.access?.readDrafts;
|
|
2784
|
+
if (readDraftsFn) {
|
|
2785
|
+
try {
|
|
2786
|
+
return !!await Promise.resolve(readDraftsFn({ req: this.buildRequestContext() }));
|
|
2787
|
+
} catch {
|
|
2788
|
+
return false;
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
const updateFn = this.collectionConfig.access?.update;
|
|
2792
|
+
if (!updateFn)
|
|
2793
|
+
return true;
|
|
2794
|
+
try {
|
|
2795
|
+
return !!await Promise.resolve(updateFn({ req: this.buildRequestContext() }));
|
|
2796
|
+
} catch {
|
|
2797
|
+
return false;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2736
2800
|
buildRequestContext() {
|
|
2737
2801
|
return {
|
|
2738
2802
|
user: this.context.user
|
|
@@ -3412,8 +3476,9 @@ function buildGraphQLSchema(collections) {
|
|
|
3412
3476
|
}
|
|
3413
3477
|
function getOrCreateEnum(field, parentName) {
|
|
3414
3478
|
const key = `${parentName}_${field.name}`;
|
|
3415
|
-
|
|
3416
|
-
|
|
3479
|
+
const cached = enumCache.get(key);
|
|
3480
|
+
if (cached) {
|
|
3481
|
+
return cached;
|
|
3417
3482
|
}
|
|
3418
3483
|
const values = {};
|
|
3419
3484
|
for (const opt of field.options) {
|
|
@@ -3524,8 +3589,9 @@ function buildGraphQLSchema(collections) {
|
|
|
3524
3589
|
}
|
|
3525
3590
|
}
|
|
3526
3591
|
function getOrCreateObjectType(fields, typeName) {
|
|
3527
|
-
|
|
3528
|
-
|
|
3592
|
+
const cachedType = typeCache.get(typeName);
|
|
3593
|
+
if (cachedType) {
|
|
3594
|
+
return cachedType;
|
|
3529
3595
|
}
|
|
3530
3596
|
const objType = new import_graphql2.GraphQLObjectType({
|
|
3531
3597
|
name: typeName,
|
|
@@ -3546,8 +3612,9 @@ function buildGraphQLSchema(collections) {
|
|
|
3546
3612
|
}
|
|
3547
3613
|
function getOrCreateInputType(fields, typeName) {
|
|
3548
3614
|
const inputName = `${typeName}Input`;
|
|
3549
|
-
|
|
3550
|
-
|
|
3615
|
+
const cachedInput = inputCache.get(inputName);
|
|
3616
|
+
if (cachedInput) {
|
|
3617
|
+
return cachedInput;
|
|
3551
3618
|
}
|
|
3552
3619
|
const inputType = new import_graphql2.GraphQLInputObjectType({
|
|
3553
3620
|
name: inputName,
|
package/index.js
CHANGED
|
@@ -1419,6 +1419,14 @@ var MediaCollection = defineCollection({
|
|
|
1419
1419
|
}
|
|
1420
1420
|
});
|
|
1421
1421
|
|
|
1422
|
+
// libs/core/src/lib/versions/version.types.ts
|
|
1423
|
+
function hasVersionDrafts(collection) {
|
|
1424
|
+
const v = collection.versions;
|
|
1425
|
+
if (!v || typeof v === "boolean")
|
|
1426
|
+
return false;
|
|
1427
|
+
return !!v.drafts;
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1422
1430
|
// libs/server-core/src/lib/field-access.ts
|
|
1423
1431
|
function hasFieldAccessControl(fields) {
|
|
1424
1432
|
for (const field of fields) {
|
|
@@ -2005,13 +2013,18 @@ var VersionOperationsImpl = class {
|
|
|
2005
2013
|
}
|
|
2006
2014
|
return "draft";
|
|
2007
2015
|
}
|
|
2008
|
-
async compare(versionId1, versionId2) {
|
|
2016
|
+
async compare(versionId1, versionId2, parentId) {
|
|
2009
2017
|
await this.checkAccess("readVersions");
|
|
2010
2018
|
const version1 = await this.findVersionById(versionId1);
|
|
2011
2019
|
const version2 = await this.findVersionById(versionId2);
|
|
2012
2020
|
if (!version1 || !version2) {
|
|
2013
2021
|
throw new Error("One or both versions not found");
|
|
2014
2022
|
}
|
|
2023
|
+
if (parentId) {
|
|
2024
|
+
if (version1.parent !== parentId || version2.parent !== parentId) {
|
|
2025
|
+
throw new Error("Version does not belong to the specified document");
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2015
2028
|
const data1 = version1.version;
|
|
2016
2029
|
const data2 = version2.version;
|
|
2017
2030
|
if (!isRecord(data1) || !isRecord(data2)) {
|
|
@@ -2066,6 +2079,9 @@ var VersionOperationsImpl = class {
|
|
|
2066
2079
|
// Private Helpers
|
|
2067
2080
|
// ============================================
|
|
2068
2081
|
async checkAccess(operation) {
|
|
2082
|
+
if (this.context.overrideAccess) {
|
|
2083
|
+
return;
|
|
2084
|
+
}
|
|
2069
2085
|
const accessFn = this.collectionConfig.access?.[operation];
|
|
2070
2086
|
if (!accessFn) {
|
|
2071
2087
|
return;
|
|
@@ -2222,6 +2238,12 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2222
2238
|
Object.assign(whereParams, constraints);
|
|
2223
2239
|
}
|
|
2224
2240
|
}
|
|
2241
|
+
if (hasVersionDrafts(this.collectionConfig) && !this.context.overrideAccess) {
|
|
2242
|
+
const canSeeDrafts = await this.canReadDrafts();
|
|
2243
|
+
if (!canSeeDrafts) {
|
|
2244
|
+
whereParams["_status"] = "published";
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2225
2247
|
const query = {
|
|
2226
2248
|
...queryOptions,
|
|
2227
2249
|
...whereParams,
|
|
@@ -2284,6 +2306,15 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2284
2306
|
doc[softDeleteField]) {
|
|
2285
2307
|
return null;
|
|
2286
2308
|
}
|
|
2309
|
+
if (hasVersionDrafts(this.collectionConfig) && !this.context.overrideAccess) {
|
|
2310
|
+
const canSeeDrafts = await this.canReadDrafts();
|
|
2311
|
+
if (!canSeeDrafts) {
|
|
2312
|
+
const record = doc;
|
|
2313
|
+
if (record["_status"] !== "published") {
|
|
2314
|
+
return null;
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2287
2318
|
if (!this.matchesDefaultWhereConstraints(doc)) {
|
|
2288
2319
|
return null;
|
|
2289
2320
|
}
|
|
@@ -2369,6 +2400,15 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2369
2400
|
if (!this.matchesDefaultWhereConstraints(originalDoc)) {
|
|
2370
2401
|
throw new DocumentNotFoundError(this.slug, id);
|
|
2371
2402
|
}
|
|
2403
|
+
if (hasVersionDrafts(this.collectionConfig) && this.adapter.createVersion) {
|
|
2404
|
+
const status = originalDoc["_status"] ?? "draft";
|
|
2405
|
+
await this.adapter.createVersion(this.slug, id, originalDoc, { status });
|
|
2406
|
+
const versionsConfig = this.collectionConfig.versions;
|
|
2407
|
+
const maxPerDoc = typeof versionsConfig === "object" && versionsConfig !== null ? versionsConfig.maxPerDoc : void 0;
|
|
2408
|
+
if (maxPerDoc && this.adapter.deleteVersions) {
|
|
2409
|
+
await this.adapter.deleteVersions(this.slug, id, maxPerDoc);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2372
2412
|
let processedData = data;
|
|
2373
2413
|
const softDeleteField = getSoftDeleteField(this.collectionConfig);
|
|
2374
2414
|
if (softDeleteField && softDeleteField in processedData) {
|
|
@@ -2700,6 +2740,30 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
2700
2740
|
throw new AccessDeniedError(operation, this.slug);
|
|
2701
2741
|
}
|
|
2702
2742
|
}
|
|
2743
|
+
/**
|
|
2744
|
+
* Check if the current user can see draft documents (non-throwing).
|
|
2745
|
+
* Uses `access.readDrafts` if configured, otherwise falls back to `access.update`.
|
|
2746
|
+
*/
|
|
2747
|
+
async canReadDrafts() {
|
|
2748
|
+
if (!this.context.user)
|
|
2749
|
+
return false;
|
|
2750
|
+
const readDraftsFn = this.collectionConfig.access?.readDrafts;
|
|
2751
|
+
if (readDraftsFn) {
|
|
2752
|
+
try {
|
|
2753
|
+
return !!await Promise.resolve(readDraftsFn({ req: this.buildRequestContext() }));
|
|
2754
|
+
} catch {
|
|
2755
|
+
return false;
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
const updateFn = this.collectionConfig.access?.update;
|
|
2759
|
+
if (!updateFn)
|
|
2760
|
+
return true;
|
|
2761
|
+
try {
|
|
2762
|
+
return !!await Promise.resolve(updateFn({ req: this.buildRequestContext() }));
|
|
2763
|
+
} catch {
|
|
2764
|
+
return false;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2703
2767
|
buildRequestContext() {
|
|
2704
2768
|
return {
|
|
2705
2769
|
user: this.context.user
|
|
@@ -3392,8 +3456,9 @@ function buildGraphQLSchema(collections) {
|
|
|
3392
3456
|
}
|
|
3393
3457
|
function getOrCreateEnum(field, parentName) {
|
|
3394
3458
|
const key = `${parentName}_${field.name}`;
|
|
3395
|
-
|
|
3396
|
-
|
|
3459
|
+
const cached = enumCache.get(key);
|
|
3460
|
+
if (cached) {
|
|
3461
|
+
return cached;
|
|
3397
3462
|
}
|
|
3398
3463
|
const values = {};
|
|
3399
3464
|
for (const opt of field.options) {
|
|
@@ -3504,8 +3569,9 @@ function buildGraphQLSchema(collections) {
|
|
|
3504
3569
|
}
|
|
3505
3570
|
}
|
|
3506
3571
|
function getOrCreateObjectType(fields, typeName) {
|
|
3507
|
-
|
|
3508
|
-
|
|
3572
|
+
const cachedType = typeCache.get(typeName);
|
|
3573
|
+
if (cachedType) {
|
|
3574
|
+
return cachedType;
|
|
3509
3575
|
}
|
|
3510
3576
|
const objType = new GraphQLObjectType({
|
|
3511
3577
|
name: typeName,
|
|
@@ -3526,8 +3592,9 @@ function buildGraphQLSchema(collections) {
|
|
|
3526
3592
|
}
|
|
3527
3593
|
function getOrCreateInputType(fields, typeName) {
|
|
3528
3594
|
const inputName = `${typeName}Input`;
|
|
3529
|
-
|
|
3530
|
-
|
|
3595
|
+
const cachedInput = inputCache.get(inputName);
|
|
3596
|
+
if (cachedInput) {
|
|
3597
|
+
return cachedInput;
|
|
3531
3598
|
}
|
|
3532
3599
|
const inputType = new GraphQLInputObjectType({
|
|
3533
3600
|
name: inputName,
|