@nexpress/core 0.3.0 → 0.3.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/dist/auth.d.ts +26 -3
- package/dist/auth.js +5 -3
- package/dist/{can-FKIEV54H.js → can-UJ2NAOIR.js} +3 -3
- package/dist/{chunk-CHQJG4BB.js → chunk-2N53KKIL.js} +2 -2
- package/dist/{chunk-DWG3RZH2.js → chunk-2VZZ7M26.js} +2 -2
- package/dist/{chunk-TD4KNSEP.js → chunk-56ZK5PWM.js} +19 -19
- package/dist/{chunk-HTDDXBMY.js → chunk-6IEYOY2L.js} +28 -121
- package/dist/chunk-6IEYOY2L.js.map +1 -0
- package/dist/{chunk-QYP6E5FP.js → chunk-6UV2P5MW.js} +63 -50
- package/dist/chunk-6UV2P5MW.js.map +1 -0
- package/dist/{chunk-26RYBFTF.js → chunk-AEKO4MXK.js} +21 -4
- package/dist/chunk-AEKO4MXK.js.map +1 -0
- package/dist/{chunk-QSJIKNQJ.js → chunk-DKOCKZVG.js} +9 -9
- package/dist/{chunk-4EPNN4XG.js → chunk-HUESWYZJ.js} +2 -2
- package/dist/{chunk-DPRBHEHI.js → chunk-HVHV3IHF.js} +2 -2
- package/dist/chunk-P5WGQRSG.js +180 -0
- package/dist/chunk-P5WGQRSG.js.map +1 -0
- package/dist/{chunk-HM46WM45.js → chunk-RDTTK27V.js} +6 -6
- package/dist/{chunk-PQBJWZ7D.js → chunk-RJ76SKWQ.js} +4 -4
- package/dist/{chunk-74CGJJDY.js → chunk-RKM4GDWM.js} +1 -1
- package/dist/{chunk-EVOBLRHW.js → chunk-UIQYA3Y7.js} +5 -5
- package/dist/{chunk-CKT4QZDC.js → chunk-WJJ5MBH5.js} +5 -5
- package/dist/community.d.ts +1 -1
- package/dist/community.js +20 -19
- package/dist/{config-2Y2YGD25.js → config-44MFLLIX.js} +8 -7
- package/dist/db-schema.d.ts +2 -2
- package/dist/db.d.ts +3 -3
- package/dist/db.js +1 -1
- package/dist/fields.d.ts +54 -0
- package/dist/fields.js +14 -0
- package/dist/{host-YSADWU7J.js → host-DKOWZWKA.js} +6 -5
- package/dist/i18n.d.ts +1 -1
- package/dist/i18n.js +1 -1
- package/dist/{index-CXuqW_Gl.d.ts → index-BmR3Z8Y5.d.ts} +1 -1
- package/dist/{index-9IhVtFfA.d.ts → index-C-jKU1St.d.ts} +2 -2
- package/dist/{index-ClcinJ29.d.ts → index-Ca-WUDH5.d.ts} +1 -1
- package/dist/{index-7_PBSyOq.d.ts → index-lACZ9sON.d.ts} +1 -1
- package/dist/index.d.ts +10 -12
- package/dist/index.js +185 -75
- package/dist/index.js.map +1 -1
- package/dist/jobs.d.ts +2 -2
- package/dist/jobs.js +2 -2
- package/dist/media.d.ts +2 -2
- package/dist/media.js +2 -2
- package/dist/{mentions-NCQR4B72.js → mentions-U4JACYI6.js} +3 -3
- package/dist/{mutes-FJSSU2JP.js → mutes-MNQP6ACF.js} +3 -3
- package/dist/{scheduled-F77QCDOG.js → scheduled-VEOGI5EW.js} +7 -6
- package/dist/seo.js +6 -5
- package/dist/{settings-JODDWMDB.js → settings-OZWM6L2K.js} +2 -2
- package/dist/settings-OZWM6L2K.js.map +1 -0
- package/dist/strings-4EWJYDOG.js +1 -1
- package/dist/{types-DI3gxsiY.d.ts → types-BY1UmEiY.d.ts} +249 -2
- package/package.json +6 -1
- package/dist/chunk-26RYBFTF.js.map +0 -1
- package/dist/chunk-HTDDXBMY.js.map +0 -1
- package/dist/chunk-QYP6E5FP.js.map +0 -1
- /package/dist/{can-FKIEV54H.js.map → can-UJ2NAOIR.js.map} +0 -0
- /package/dist/{chunk-CHQJG4BB.js.map → chunk-2N53KKIL.js.map} +0 -0
- /package/dist/{chunk-DWG3RZH2.js.map → chunk-2VZZ7M26.js.map} +0 -0
- /package/dist/{chunk-TD4KNSEP.js.map → chunk-56ZK5PWM.js.map} +0 -0
- /package/dist/{chunk-QSJIKNQJ.js.map → chunk-DKOCKZVG.js.map} +0 -0
- /package/dist/{chunk-4EPNN4XG.js.map → chunk-HUESWYZJ.js.map} +0 -0
- /package/dist/{chunk-DPRBHEHI.js.map → chunk-HVHV3IHF.js.map} +0 -0
- /package/dist/{chunk-HM46WM45.js.map → chunk-RDTTK27V.js.map} +0 -0
- /package/dist/{chunk-PQBJWZ7D.js.map → chunk-RJ76SKWQ.js.map} +0 -0
- /package/dist/{chunk-74CGJJDY.js.map → chunk-RKM4GDWM.js.map} +0 -0
- /package/dist/{chunk-EVOBLRHW.js.map → chunk-UIQYA3Y7.js.map} +0 -0
- /package/dist/{chunk-CKT4QZDC.js.map → chunk-WJJ5MBH5.js.map} +0 -0
- /package/dist/{config-2Y2YGD25.js.map → config-44MFLLIX.js.map} +0 -0
- /package/dist/{host-YSADWU7J.js.map → fields.js.map} +0 -0
- /package/dist/{mentions-NCQR4B72.js.map → host-DKOWZWKA.js.map} +0 -0
- /package/dist/{mutes-FJSSU2JP.js.map → mentions-U4JACYI6.js.map} +0 -0
- /package/dist/{scheduled-F77QCDOG.js.map → mutes-MNQP6ACF.js.map} +0 -0
- /package/dist/{settings-JODDWMDB.js.map → scheduled-VEOGI5EW.js.map} +0 -0
package/dist/jobs.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { D as DEFAULT_JOB_LOG_RETENTION_MS, L as ListJobLogsOptions, N as NpJobCountOptions, a as NpJobHandler, b as NpJobListOptions, c as NpJobListResult, d as NpJobLogEntry, e as NpJobQueue, f as NpJobState, g as NpJobStateCounts, h as NpJobSummary, i as NpJobsPauseState, j as NpPluginScheduleStats, k as NpReconcileSchedulesResult, l as NpScheduleSummary, m as NpWorkerHealthSummary, n as NpWorkerHeartbeat, P as PAUSE_SYNC_INTERVAL_MS, o as PgBossAdapter, S as SetJobsPauseStateInput, W as WORKER_HEARTBEAT_INTERVAL_MS, p as WORKER_STALE_THRESHOLD_MS, r as countAliveWorkers, s as countJobLogs, t as enqueueJob, u as getAllJobHandlers, v as getCurrentJobId, w as getJobHandler, x as getJobQueue, y as getJobsPauseState, z as getOptionalJobQueue, A as listJobLogs, B as listWorkerHealth, C as markWorkerStopped, E as pruneJobLogsOlderThan, F as purgeStaleWorkers, G as recordHeartbeat, H as recordJobLog, I as registerBuiltinHandlers, J as registerJobHandler, K as runInJobContext, M as setJobQueue, O as setJobsPauseState, Q as startProducer, R as startWorker, T as stopProducer, U as stopWorker } from './index-
|
|
2
|
-
import './types-
|
|
1
|
+
export { D as DEFAULT_JOB_LOG_RETENTION_MS, L as ListJobLogsOptions, N as NpJobCountOptions, a as NpJobHandler, b as NpJobListOptions, c as NpJobListResult, d as NpJobLogEntry, e as NpJobQueue, f as NpJobState, g as NpJobStateCounts, h as NpJobSummary, i as NpJobsPauseState, j as NpPluginScheduleStats, k as NpReconcileSchedulesResult, l as NpScheduleSummary, m as NpWorkerHealthSummary, n as NpWorkerHeartbeat, P as PAUSE_SYNC_INTERVAL_MS, o as PgBossAdapter, S as SetJobsPauseStateInput, W as WORKER_HEARTBEAT_INTERVAL_MS, p as WORKER_STALE_THRESHOLD_MS, r as countAliveWorkers, s as countJobLogs, t as enqueueJob, u as getAllJobHandlers, v as getCurrentJobId, w as getJobHandler, x as getJobQueue, y as getJobsPauseState, z as getOptionalJobQueue, A as listJobLogs, B as listWorkerHealth, C as markWorkerStopped, E as pruneJobLogsOlderThan, F as purgeStaleWorkers, G as recordHeartbeat, H as recordJobLog, I as registerBuiltinHandlers, J as registerJobHandler, K as runInJobContext, M as setJobQueue, O as setJobsPauseState, Q as startProducer, R as startWorker, T as stopProducer, U as stopWorker } from './index-lACZ9sON.js';
|
|
2
|
+
import './types-BY1UmEiY.js';
|
|
3
3
|
import 'pg-boss';
|
|
4
4
|
import './logger-DqGaOU_j.js';
|
package/dist/jobs.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
startWorker,
|
|
19
19
|
stopProducer,
|
|
20
20
|
stopWorker
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-DKOCKZVG.js";
|
|
22
22
|
import {
|
|
23
23
|
DEFAULT_JOB_LOG_RETENTION_MS,
|
|
24
24
|
countJobLogs,
|
|
@@ -29,13 +29,13 @@ import {
|
|
|
29
29
|
runInJobContext
|
|
30
30
|
} from "./chunk-QBIJZZ5V.js";
|
|
31
31
|
import "./chunk-LSHHRDVR.js";
|
|
32
|
-
import "./chunk-WV272MPW.js";
|
|
33
32
|
import {
|
|
34
33
|
enqueueJob,
|
|
35
34
|
getJobQueue,
|
|
36
35
|
getOptionalJobQueue,
|
|
37
36
|
setJobQueue
|
|
38
37
|
} from "./chunk-V2UNHGAP.js";
|
|
38
|
+
import "./chunk-WV272MPW.js";
|
|
39
39
|
import "./chunk-OROPGO65.js";
|
|
40
40
|
import "./chunk-NFHS7CFV.js";
|
|
41
41
|
import "./chunk-XANPEOJC.js";
|
package/dist/media.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { D as DEFAULT_IMAGE_SIZES, q as DrizzleTransactionLike, b as NpGetMediaUrlOptions, c as NpMediaUploader, d as NpMediaUploaderKindFilter, e as NpMediaVariantName, f as NpProcessedImageResult, r as NpProcessedImageSourceMetadata, g as NpProcessedImageVariant, h as cleanupDeletedMedia, i as deleteMedia, j as extractMediaIds, k as getMediaById, l as getMediaUrl, m as getStorageAdapter, n as listMedia, p as processImage, o as processMediaImage, s as setStorageAdapter, t as syncMediaRefs, u as uploadMedia } from './index-
|
|
2
|
-
import './types-
|
|
1
|
+
export { D as DEFAULT_IMAGE_SIZES, q as DrizzleTransactionLike, b as NpGetMediaUrlOptions, c as NpMediaUploader, d as NpMediaUploaderKindFilter, e as NpMediaVariantName, f as NpProcessedImageResult, r as NpProcessedImageSourceMetadata, g as NpProcessedImageVariant, h as cleanupDeletedMedia, i as deleteMedia, j as extractMediaIds, k as getMediaById, l as getMediaUrl, m as getStorageAdapter, n as listMedia, p as processImage, o as processMediaImage, s as setStorageAdapter, t as syncMediaRefs, u as uploadMedia } from './index-Ca-WUDH5.js';
|
|
2
|
+
import './types-BY1UmEiY.js';
|
|
3
3
|
import 'drizzle-orm';
|
|
4
4
|
import 'drizzle-orm/pg-core';
|
|
5
5
|
import 'node:stream/web';
|
package/dist/media.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-TETTWT56.js";
|
|
5
5
|
import {
|
|
6
6
|
getMediaUrl
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2N53KKIL.js";
|
|
8
8
|
import {
|
|
9
9
|
DEFAULT_IMAGE_SIZES,
|
|
10
10
|
cleanupDeletedMedia,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
processMediaImage,
|
|
17
17
|
setStorageAdapter,
|
|
18
18
|
uploadMedia
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-2VZZ7M26.js";
|
|
20
20
|
import "./chunk-V2UNHGAP.js";
|
|
21
21
|
import "./chunk-OROPGO65.js";
|
|
22
22
|
import "./chunk-NFHS7CFV.js";
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
extractMentionHandlesFromRichText,
|
|
6
6
|
fanOutMentionNotifications,
|
|
7
7
|
resolveMentionedMembers
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-RDTTK27V.js";
|
|
9
9
|
import "./chunk-U4QCCLAW.js";
|
|
10
|
-
import "./chunk-ZCINJSS4.js";
|
|
11
10
|
import "./chunk-SBCVAC2Z.js";
|
|
11
|
+
import "./chunk-ZCINJSS4.js";
|
|
12
12
|
import "./chunk-XANPEOJC.js";
|
|
13
13
|
import "./chunk-X7K5F2UI.js";
|
|
14
14
|
import "./chunk-PZ5AY32C.js";
|
|
@@ -20,4 +20,4 @@ export {
|
|
|
20
20
|
fanOutMentionNotifications,
|
|
21
21
|
resolveMentionedMembers
|
|
22
22
|
};
|
|
23
|
-
//# sourceMappingURL=mentions-
|
|
23
|
+
//# sourceMappingURL=mentions-U4JACYI6.js.map
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
listMutes,
|
|
5
5
|
muteMember,
|
|
6
6
|
unmuteMember
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-WJJ5MBH5.js";
|
|
8
8
|
import "./chunk-U4QCCLAW.js";
|
|
9
|
-
import "./chunk-ZCINJSS4.js";
|
|
10
9
|
import "./chunk-SBCVAC2Z.js";
|
|
10
|
+
import "./chunk-ZCINJSS4.js";
|
|
11
11
|
import "./chunk-XANPEOJC.js";
|
|
12
12
|
import "./chunk-X7K5F2UI.js";
|
|
13
13
|
import "./chunk-PZ5AY32C.js";
|
|
@@ -18,4 +18,4 @@ export {
|
|
|
18
18
|
muteMember,
|
|
19
19
|
unmuteMember
|
|
20
20
|
};
|
|
21
|
-
//# sourceMappingURL=mutes-
|
|
21
|
+
//# sourceMappingURL=mutes-MNQP6ACF.js.map
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
publishScheduledDocuments
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-HUESWYZJ.js";
|
|
4
|
+
import "./chunk-6IEYOY2L.js";
|
|
5
|
+
import "./chunk-2VZZ7M26.js";
|
|
6
|
+
import "./chunk-P5WGQRSG.js";
|
|
5
7
|
import "./chunk-4ZLMEKFX.js";
|
|
6
8
|
import "./chunk-U4QCCLAW.js";
|
|
7
|
-
import "./chunk-ZCINJSS4.js";
|
|
8
9
|
import "./chunk-SBCVAC2Z.js";
|
|
9
|
-
import "./chunk-
|
|
10
|
-
import "./chunk-DWG3RZH2.js";
|
|
10
|
+
import "./chunk-ZCINJSS4.js";
|
|
11
11
|
import "./chunk-V2UNHGAP.js";
|
|
12
|
+
import "./chunk-WV272MPW.js";
|
|
12
13
|
import "./chunk-OROPGO65.js";
|
|
13
14
|
import "./chunk-NFHS7CFV.js";
|
|
14
15
|
import "./chunk-XANPEOJC.js";
|
|
@@ -17,4 +18,4 @@ import "./chunk-PZ5AY32C.js";
|
|
|
17
18
|
export {
|
|
18
19
|
publishScheduledDocuments
|
|
19
20
|
};
|
|
20
|
-
//# sourceMappingURL=scheduled-
|
|
21
|
+
//# sourceMappingURL=scheduled-VEOGI5EW.js.map
|
package/dist/seo.js
CHANGED
|
@@ -12,15 +12,16 @@ import {
|
|
|
12
12
|
renderSitemapIndexXml,
|
|
13
13
|
renderSitemapXml,
|
|
14
14
|
validateSeoSettingsPatch
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
} from "./chunk-HVHV3IHF.js";
|
|
16
|
+
import "./chunk-6IEYOY2L.js";
|
|
17
|
+
import "./chunk-2VZZ7M26.js";
|
|
18
|
+
import "./chunk-P5WGQRSG.js";
|
|
17
19
|
import "./chunk-4ZLMEKFX.js";
|
|
18
20
|
import "./chunk-U4QCCLAW.js";
|
|
19
|
-
import "./chunk-ZCINJSS4.js";
|
|
20
21
|
import "./chunk-SBCVAC2Z.js";
|
|
21
|
-
import "./chunk-
|
|
22
|
-
import "./chunk-DWG3RZH2.js";
|
|
22
|
+
import "./chunk-ZCINJSS4.js";
|
|
23
23
|
import "./chunk-V2UNHGAP.js";
|
|
24
|
+
import "./chunk-WV272MPW.js";
|
|
24
25
|
import "./chunk-OROPGO65.js";
|
|
25
26
|
import "./chunk-NFHS7CFV.js";
|
|
26
27
|
import "./chunk-XANPEOJC.js";
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
getCommunitySettings,
|
|
4
4
|
updateCommunitySettings,
|
|
5
5
|
validateCommunitySettingsPatch
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-RKM4GDWM.js";
|
|
7
7
|
import "./chunk-ZCINJSS4.js";
|
|
8
8
|
import "./chunk-XANPEOJC.js";
|
|
9
9
|
import "./chunk-X7K5F2UI.js";
|
|
@@ -14,4 +14,4 @@ export {
|
|
|
14
14
|
updateCommunitySettings,
|
|
15
15
|
validateCommunitySettingsPatch
|
|
16
16
|
};
|
|
17
|
-
//# sourceMappingURL=settings-
|
|
17
|
+
//# sourceMappingURL=settings-OZWM6L2K.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/strings-4EWJYDOG.js
CHANGED
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
} from "./chunk-ELK6AVW5.js";
|
|
11
11
|
import "./chunk-4ZLMEKFX.js";
|
|
12
12
|
import "./chunk-U4QCCLAW.js";
|
|
13
|
-
import "./chunk-ZCINJSS4.js";
|
|
14
13
|
import "./chunk-SBCVAC2Z.js";
|
|
14
|
+
import "./chunk-ZCINJSS4.js";
|
|
15
15
|
import "./chunk-NFHS7CFV.js";
|
|
16
16
|
import "./chunk-XANPEOJC.js";
|
|
17
17
|
import "./chunk-X7K5F2UI.js";
|
|
@@ -32,7 +32,54 @@ type NpAccessFunction = (args: {
|
|
|
32
32
|
doc?: Record<string, unknown>;
|
|
33
33
|
data?: Record<string, unknown>;
|
|
34
34
|
}) => boolean | Promise<boolean>;
|
|
35
|
+
/**
|
|
36
|
+
* Free-form predicate. Server-only — functions don't survive the
|
|
37
|
+
* server→client boundary in Next.js (the framework's
|
|
38
|
+
* `toClientCollectionConfig` strips them). For conditions that
|
|
39
|
+
* need to run in the admin editor's browser-side renderer, use
|
|
40
|
+
* `NpFieldConditionExpr` (serializable JSON shape) instead.
|
|
41
|
+
*/
|
|
35
42
|
type NpFieldCondition = (data: Record<string, unknown>, siblingData: Record<string, unknown>) => boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Serializable condition predicate. Evaluated client-side (admin
|
|
45
|
+
* editor) AND server-side (pipeline validation) from the same
|
|
46
|
+
* declaration — survives the RSC serialization boundary because
|
|
47
|
+
* it's plain JSON.
|
|
48
|
+
*
|
|
49
|
+
* Examples:
|
|
50
|
+
* `{ when: "kind", equals: "doc" }`
|
|
51
|
+
* `{ when: "kind", notEquals: "doc" }`
|
|
52
|
+
* `{ when: "kind", in: ["doc", "page"] }`
|
|
53
|
+
* `{ when: "wpOriginalAuthor", exists: true }`
|
|
54
|
+
* `{ all: [{ when: "kind", equals: "doc" }, { when: "publishedAt", exists: true }] }`
|
|
55
|
+
* `{ any: [{ when: "kind", equals: "doc" }, { when: "kind", equals: "page" }] }`
|
|
56
|
+
*
|
|
57
|
+
* `exists: true` returns true when the value is defined, not null,
|
|
58
|
+
* not the empty string, and not an empty array. `exists: false`
|
|
59
|
+
* is the inverse. `equals` / `notEquals` use strict equality.
|
|
60
|
+
* `in` / `notIn` check membership against an unknown[] list.
|
|
61
|
+
* `all` / `any` are AND / OR over a list of nested expressions.
|
|
62
|
+
*/
|
|
63
|
+
type NpFieldConditionExpr = {
|
|
64
|
+
when: string;
|
|
65
|
+
equals: unknown;
|
|
66
|
+
} | {
|
|
67
|
+
when: string;
|
|
68
|
+
notEquals: unknown;
|
|
69
|
+
} | {
|
|
70
|
+
when: string;
|
|
71
|
+
in: unknown[];
|
|
72
|
+
} | {
|
|
73
|
+
when: string;
|
|
74
|
+
notIn: unknown[];
|
|
75
|
+
} | {
|
|
76
|
+
when: string;
|
|
77
|
+
exists: boolean;
|
|
78
|
+
} | {
|
|
79
|
+
all: NpFieldConditionExpr[];
|
|
80
|
+
} | {
|
|
81
|
+
any: NpFieldConditionExpr[];
|
|
82
|
+
};
|
|
36
83
|
type NpFieldValidator = (value: unknown, args: {
|
|
37
84
|
data: Record<string, unknown>;
|
|
38
85
|
siblingData: Record<string, unknown>;
|
|
@@ -51,7 +98,7 @@ interface NpFieldBase {
|
|
|
51
98
|
description?: string;
|
|
52
99
|
placeholder?: string;
|
|
53
100
|
readOnly?: boolean;
|
|
54
|
-
condition?: NpFieldCondition;
|
|
101
|
+
condition?: NpFieldCondition | NpFieldConditionExpr;
|
|
55
102
|
width?: string;
|
|
56
103
|
/**
|
|
57
104
|
* Optional override for the admin field renderer. The default
|
|
@@ -83,6 +130,35 @@ interface NpFieldBase {
|
|
|
83
130
|
* surfacing a date input in the primary column.
|
|
84
131
|
*/
|
|
85
132
|
position?: "main" | "sidebar";
|
|
133
|
+
/**
|
|
134
|
+
* Sidebar grouping label. Sidebar fields with the same
|
|
135
|
+
* `group` render together in one collapsible Card with the
|
|
136
|
+
* group name as the title. Fields with no group fall into
|
|
137
|
+
* the default "Publish" Card. Group order in the rendered
|
|
138
|
+
* sidebar follows first-seen order in the collection's
|
|
139
|
+
* `fields` array — operators control layout by ordering.
|
|
140
|
+
*
|
|
141
|
+
* Examples: `"Publish"`, `"Lead"`, `"Taxonomy"`, `"Author"`,
|
|
142
|
+
* `"Hierarchy"`, `"SEO"`. The group label is the visible
|
|
143
|
+
* Card title (not a slug), so it doesn't have to be
|
|
144
|
+
* machine-friendly.
|
|
145
|
+
*
|
|
146
|
+
* Only meaningful when `position: "sidebar"`. Main-column
|
|
147
|
+
* fields ignore this — they render in field-array order
|
|
148
|
+
* without grouping.
|
|
149
|
+
*/
|
|
150
|
+
group?: string;
|
|
151
|
+
/**
|
|
152
|
+
* The id of the theme whose `requires.collections.<slug>.fields`
|
|
153
|
+
* contributed this field. Stamped by `mergeThemeRequirements`;
|
|
154
|
+
* never set this from operator config. Same convention as
|
|
155
|
+
* `admin._themeOrigin` at the collection level and per-`kinds`
|
|
156
|
+
* entry: when an operator switches to a different active
|
|
157
|
+
* theme, the admin filters out fields whose origin doesn't
|
|
158
|
+
* match. Operator-declared fields carry no origin and always
|
|
159
|
+
* pass through.
|
|
160
|
+
*/
|
|
161
|
+
_themeOrigin?: string;
|
|
86
162
|
};
|
|
87
163
|
validate?: NpFieldValidator;
|
|
88
164
|
}
|
|
@@ -379,6 +455,34 @@ interface NpCollectionConfig {
|
|
|
379
455
|
* before merge) keep this unset and always show.
|
|
380
456
|
*/
|
|
381
457
|
_themeOrigin?: string;
|
|
458
|
+
/**
|
|
459
|
+
* Framework-set. Stamped by `mergeThemeRequirements` from
|
|
460
|
+
* `theme.manifest.requires.collections.<slug>.kinds`,
|
|
461
|
+
* unioned across all registered themes. The admin sidebar
|
|
462
|
+
* walks this map to render per-kind entries under "Content"
|
|
463
|
+
* (universal-content-model #748).
|
|
464
|
+
*
|
|
465
|
+
* Keyed by the discriminator value declared on the
|
|
466
|
+
* `kind` field's options. Empty / missing → admin shows a
|
|
467
|
+
* single collection entry like before.
|
|
468
|
+
*/
|
|
469
|
+
kinds?: Record<string, NpThemeCollectionKind>;
|
|
470
|
+
/**
|
|
471
|
+
* Visual metadata for sidebar field groups. Keyed by the
|
|
472
|
+
* `admin.group` label used on individual fields. The editor's
|
|
473
|
+
* `SidebarGroupCard` reads this to render an icon next to
|
|
474
|
+
* the group title and optionally surface a description.
|
|
475
|
+
*
|
|
476
|
+
* Operator-declared collections set this directly. Themes
|
|
477
|
+
* contribute their own group icons via
|
|
478
|
+
* `requires.collections.<slug>.groupMeta` (merged through
|
|
479
|
+
* `mergeThemeRequirements`, unioned across themes with
|
|
480
|
+
* last-write-wins on per-key props).
|
|
481
|
+
*
|
|
482
|
+
* Groups without an entry render without an icon — same
|
|
483
|
+
* behavior as before this surface existed.
|
|
484
|
+
*/
|
|
485
|
+
groupMeta?: Record<string, NpAdminGroupMeta>;
|
|
382
486
|
};
|
|
383
487
|
upload?: NpUploadConfig;
|
|
384
488
|
}
|
|
@@ -641,6 +745,107 @@ interface NpThemeCollectionRequirement {
|
|
|
641
745
|
* slug is registered. Operator-authored collections of the
|
|
642
746
|
* same slug always take precedence. */
|
|
643
747
|
createIfAbsent?: boolean;
|
|
748
|
+
/**
|
|
749
|
+
* Per-kind metadata for the `kind` discriminator field
|
|
750
|
+
* (universal-content-model #748). Themes contribute one entry
|
|
751
|
+
* per kind they author content for; the framework's auto-merge
|
|
752
|
+
* unions entries across registered themes so the admin sidebar
|
|
753
|
+
* and the public-site router both see one canonical map.
|
|
754
|
+
*
|
|
755
|
+
* Keyed by the option value declared on `fields.kind.options`
|
|
756
|
+
* (e.g. `kinds.doc` matches the option whose `value: "doc"`).
|
|
757
|
+
* The collection slug remains `posts`; kinds are a presentation
|
|
758
|
+
* split, not a separate table.
|
|
759
|
+
*
|
|
760
|
+
* The `kind` field itself doesn't have to live on this
|
|
761
|
+
* collection's `fields` for the metadata to apply — themes that
|
|
762
|
+
* extend a single kind (`fields.kind.options: [{value:"doc"}]`
|
|
763
|
+
* + `kinds.doc: {...}`) ship both together and the merge unions
|
|
764
|
+
* them with whatever other themes declare. A `kinds` block on a
|
|
765
|
+
* collection without a corresponding select field is a no-op
|
|
766
|
+
* (the admin shows the regular collection list view).
|
|
767
|
+
*/
|
|
768
|
+
kinds?: Record<string, NpThemeCollectionKind>;
|
|
769
|
+
/**
|
|
770
|
+
* Sidebar group metadata the theme contributes. Keyed by the
|
|
771
|
+
* `admin.group` label the theme uses on its contributed fields
|
|
772
|
+
* (e.g. theme-magazine contributes `Magazine: { icon: "Newspaper" }`).
|
|
773
|
+
* Merged into the collection's `admin.groupMeta` via
|
|
774
|
+
* last-write-wins union — two themes claiming the same group
|
|
775
|
+
* label get the later theme's icon / description.
|
|
776
|
+
*
|
|
777
|
+
* Declaring a group key without contributing fields with the
|
|
778
|
+
* same `admin.group` is allowed (the entry is unused but
|
|
779
|
+
* harmless) — useful for overriding a framework default's
|
|
780
|
+
* icon without adding any new fields.
|
|
781
|
+
*/
|
|
782
|
+
groupMeta?: Record<string, NpAdminGroupMeta>;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* One kind entry — admin nav + public URL metadata for a single
|
|
786
|
+
* discriminator value on a `select` field (typically `posts.kind`).
|
|
787
|
+
*
|
|
788
|
+
* Field merge: two themes declaring the same kind value get
|
|
789
|
+
* last-wins on every property. Operators rarely need to redefine
|
|
790
|
+
* a kind their theme already ships.
|
|
791
|
+
*/
|
|
792
|
+
/**
|
|
793
|
+
* Per-group sidebar metadata. Resolves at runtime in the admin
|
|
794
|
+
* edit view; not codegen'd into the DB schema.
|
|
795
|
+
*/
|
|
796
|
+
interface NpAdminGroupMeta {
|
|
797
|
+
/**
|
|
798
|
+
* Lucide icon name (no `Icon` suffix) shown next to the
|
|
799
|
+
* group title in the editor sidebar. Examples: `"Calendar"`,
|
|
800
|
+
* `"BookOpen"`, `"Briefcase"`. Resolved client-side; unknown
|
|
801
|
+
* names render no icon (silent fallback, no warning).
|
|
802
|
+
*/
|
|
803
|
+
icon?: string;
|
|
804
|
+
/**
|
|
805
|
+
* One-line description shown beneath the group title. Useful
|
|
806
|
+
* for operator hints like "Search-result preview + social
|
|
807
|
+
* card." Truncated by the admin if it's long.
|
|
808
|
+
*/
|
|
809
|
+
description?: string;
|
|
810
|
+
}
|
|
811
|
+
interface NpThemeCollectionKind {
|
|
812
|
+
/** Singular human label — "Doc", "Project", "Article". */
|
|
813
|
+
label: string;
|
|
814
|
+
/** Plural label for the admin sidebar entry — "Documentation". */
|
|
815
|
+
labelPlural: string;
|
|
816
|
+
/** Lucide icon name (no `Icon` suffix) — "BookOpen", "Briefcase". */
|
|
817
|
+
icon?: string;
|
|
818
|
+
/**
|
|
819
|
+
* Public-site URL pattern. `:slug` is the only supported param;
|
|
820
|
+
* the catch-all router (`apps/web/src/app/(site)/[[...slug]]/page.tsx`)
|
|
821
|
+
* matches the path, extracts the slug, and queries the host
|
|
822
|
+
* collection with `where: { kind: "<this-key>", slug: "<match>" }`.
|
|
823
|
+
*
|
|
824
|
+
* Omit to fall back to the framework default (`/<collection-slug>/<slug>`,
|
|
825
|
+
* shared with the kind=null catch-all path). Two kinds declaring
|
|
826
|
+
* the same urlPattern collide and the first wins; the admin
|
|
827
|
+
* surfaces this via the requirements diff.
|
|
828
|
+
*/
|
|
829
|
+
urlPattern?: string;
|
|
830
|
+
/**
|
|
831
|
+
* True → admin's per-kind list view surfaces `parent` + `order`
|
|
832
|
+
* controls and renders rows as a tree. Themes with hierarchical
|
|
833
|
+
* content (docs, sections) opt in; flat kinds leave it false.
|
|
834
|
+
*/
|
|
835
|
+
hierarchical?: boolean;
|
|
836
|
+
/**
|
|
837
|
+
* Framework-set. Stamped by `mergeThemeRequirements` with the
|
|
838
|
+
* id of the theme whose `requires.collections.<slug>.kinds`
|
|
839
|
+
* contributed this entry. The admin sidebar reads it to gate
|
|
840
|
+
* per-kind nav entries on the active theme — the bundled-themes
|
|
841
|
+
* prebake unions every built-in's kinds onto the schema, but
|
|
842
|
+
* only the active theme's kinds deserve sidebar real estate.
|
|
843
|
+
*
|
|
844
|
+
* NEVER set this by hand from operator config. The underscore
|
|
845
|
+
* is intentional — same convention as `admin._themeOrigin` at
|
|
846
|
+
* the collection level.
|
|
847
|
+
*/
|
|
848
|
+
_themeOrigin?: string;
|
|
644
849
|
}
|
|
645
850
|
/**
|
|
646
851
|
* One field's requirement. The `type` matches an `NpFieldConfig`
|
|
@@ -660,6 +865,48 @@ interface NpThemeFieldRequirement {
|
|
|
660
865
|
* severity, and a future F.8 may treat it as opt-in patch.
|
|
661
866
|
*/
|
|
662
867
|
hard?: boolean;
|
|
868
|
+
/**
|
|
869
|
+
* For `select` only — extra options to union into the existing
|
|
870
|
+
* select field. Two themes can contribute disjoint option sets
|
|
871
|
+
* (e.g. theme-docs adds `kind="doc"`, theme-portfolio adds
|
|
872
|
+
* `kind="project"`); the merge dedupes on `value` and last-wins
|
|
873
|
+
* on `label`. Universal-content-model Phase U.1 (#748).
|
|
874
|
+
*
|
|
875
|
+
* Ignored when the merge can't find an existing select with the
|
|
876
|
+
* same `name`; theme authors that need a brand-new select can't
|
|
877
|
+
* synthesise one through requirements (`NpThemeFieldRequirement`
|
|
878
|
+
* doesn't carry enough to construct a valid `NpSelectField`).
|
|
879
|
+
*/
|
|
880
|
+
options?: Array<{
|
|
881
|
+
label: string;
|
|
882
|
+
value: string;
|
|
883
|
+
}>;
|
|
884
|
+
/**
|
|
885
|
+
* Optional admin hints forwarded onto the synthesised field's
|
|
886
|
+
* `admin` slot. Themes use these to bucket their contributed
|
|
887
|
+
* fields into the right sidebar group and hide fields when
|
|
888
|
+
* irrelevant to the active kind.
|
|
889
|
+
*
|
|
890
|
+
* `group` — sidebar Card grouping label
|
|
891
|
+
* (e.g. `"Media"`, `"SEO"`).
|
|
892
|
+
* `condition` — runtime visibility gate. Either a function
|
|
893
|
+
* (server-only — stripped at the RSC boundary)
|
|
894
|
+
* or a serializable expression (works in both
|
|
895
|
+
* environments). Prefer the expression form so
|
|
896
|
+
* the admin's client renderer can re-evaluate
|
|
897
|
+
* on live form values:
|
|
898
|
+
* `{ when: "kind", equals: "doc" }`
|
|
899
|
+
* `{ when: "kind", notEquals: "doc" }`
|
|
900
|
+
* `{ when: "kind", in: ["doc", "page"] }`
|
|
901
|
+
* `{ when: "wpOriginalAuthor", exists: true }`
|
|
902
|
+
* `position` — main vs sidebar column. Defaults to the
|
|
903
|
+
* framework's `isSidebarField` heuristic.
|
|
904
|
+
*/
|
|
905
|
+
admin?: {
|
|
906
|
+
group?: string;
|
|
907
|
+
condition?: NpFieldCondition | NpFieldConditionExpr;
|
|
908
|
+
position?: "main" | "sidebar";
|
|
909
|
+
};
|
|
663
910
|
}
|
|
664
911
|
interface NpRegisteredTheme {
|
|
665
912
|
manifest: NpThemeManifest;
|
|
@@ -895,4 +1142,4 @@ interface NpSaveResult {
|
|
|
895
1142
|
*/
|
|
896
1143
|
declare const ROLE_HIERARCHY: Record<NpUserRole, number>;
|
|
897
1144
|
|
|
898
|
-
export { type
|
|
1145
|
+
export { ROLE_HIERARCHY as $, type NpEmailField as A, type NpFieldCondition as B, type NpFieldConditionExpr as C, type NpFieldValidator as D, type NpFindWhere as E, type NpFindWhereSystemTokens as F, type NpGroupField as G, type NpPrincipal as H, type NpI18nConfig as I, type NpImageSize as J, type NpJobType as K, type NpJsonField as L, type NpNumberField as M, type NpRichTextContent as N, type NpPluginContext as O, type NpRadioField as P, type NpRelationshipField as Q, type NpResolvedPluginLike as R, type NpRichTextField as S, type NpRowField as T, type NpSelectField as U, type NpTextField as V, type NpTextareaField as W, type NpThemeCollectionKind as X, type NpThemeCollectionRequirement as Y, type NpUploadConfig as Z, type NpUploadField as _, type NpNavItem as a, type NpBlockInstance as b, type NpConfig as c, type NpCollectionConfig as d, type NpAuthUser as e, type NpSaveOptions as f, type NpSaveResult as g, type NpFindOptions as h, type NpFindResult as i, type NpRegisteredTheme as j, type NpThemeFieldRequirement as k, type NpThemeManifest as l, type NpUserRole as m, type NpFieldConfig as n, type NpPluginConfig as o, type NpAccessFunction as p, type NpAdminGroupMeta as q, type NpArrayField as r, type NpBlockConfig as s, type NpBlocksField as t, type NpCheckboxField as u, type NpCollapsibleField as v, type NpCollectionHook as w, type NpDateField as x, type NpDocumentStatus as y, type NpEditorConfig as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexpress/core",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Server-side core for NexPress — collections pipeline, auth, jobs, media, plugins, observability.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Nexpress",
|
|
@@ -48,6 +48,11 @@
|
|
|
48
48
|
"import": "./dist/db.js",
|
|
49
49
|
"default": "./dist/db.js"
|
|
50
50
|
},
|
|
51
|
+
"./fields": {
|
|
52
|
+
"types": "./dist/fields.d.ts",
|
|
53
|
+
"import": "./dist/fields.js",
|
|
54
|
+
"default": "./dist/fields.js"
|
|
55
|
+
},
|
|
51
56
|
"./i18n": {
|
|
52
57
|
"types": "./dist/i18n.d.ts",
|
|
53
58
|
"import": "./dist/i18n.js",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/db/connection.ts","../src/db/generator.ts","../src/db/type-generator.ts"],"sourcesContent":["import { drizzle } from \"drizzle-orm/node-postgres\";\nimport { Pool } from \"pg\";\n\nimport { readEnvPositiveInt } from \"../config/env.js\";\nimport * as schema from \"./schema/index.js\";\n\nexport interface CreateDbConnectionConfig {\n connectionString: string;\n pool?: Pool;\n /**\n * Override Pool option defaults. Useful for tests, or for sites that need\n * to tune connection limits / timeouts. The fields explicitly set below\n * (`connectionTimeoutMillis`, `statement_timeout`) win unless callers\n * override them here.\n */\n poolOptions?: ConstructorParameters<typeof Pool>[0];\n}\n\n/**\n * Defaults chosen so a wedged Postgres (network drop, container paused,\n * lock storm) surfaces a clear error in single-digit seconds rather than\n * letting a Next.js request handler hang past the platform's request\n * deadline. Both bounds can be raised on a per-site basis via `poolOptions`\n * or globally via `NP_DB_CONNECTION_TIMEOUT_MS` / `NP_DB_STATEMENT_TIMEOUT_MS`.\n *\n * - `connectionTimeoutMillis` caps `pool.connect()` waits — kicks in when\n * the daemon TCP-accepts but never completes the Postgres handshake (the\n * Docker-Desktop-stuck failure mode).\n * - `statement_timeout` is enforced server-side by Postgres and bounds any\n * single query, including the catch-all \"select * from np_users where\n * email = $1\" path that has to be fast on the auth hot path. Sites with\n * legitimately heavy admin workloads (large audit searches, bulk\n * exports) raise this rather than dropping the bound entirely.\n */\nconst DEFAULT_CONNECTION_TIMEOUT_MS = readEnvPositiveInt(\"NP_DB_CONNECTION_TIMEOUT_MS\", 5_000);\nconst DEFAULT_STATEMENT_TIMEOUT_MS = readEnvPositiveInt(\"NP_DB_STATEMENT_TIMEOUT_MS\", 10_000);\n\nexport function createDbConnection(config: CreateDbConnectionConfig) {\n const pool =\n config.pool ??\n new Pool({\n connectionString: config.connectionString,\n connectionTimeoutMillis: DEFAULT_CONNECTION_TIMEOUT_MS,\n statement_timeout: DEFAULT_STATEMENT_TIMEOUT_MS,\n ...config.poolOptions,\n });\n\n return drizzle(pool, { schema });\n}\n","import {\n type NpArrayField,\n type NpCollectionConfig,\n type NpFieldConfig,\n type NpRelationshipField,\n} from \"../config/types.js\";\n\ninterface TableRelation {\n key: string;\n kind: \"one\" | \"many\";\n targetIdentifier: string;\n fields?: string[];\n references?: string[];\n}\n\ninterface GeneratedTable {\n identifier: string;\n tableName: string;\n columns: string[];\n indexes: string[];\n relations: TableRelation[];\n}\n\ninterface ScalarFieldResult {\n columns: string[];\n relations: TableRelation[];\n}\n\ninterface TableContext {\n collectionSlug: string;\n tableIdentifier: string;\n tableName: string;\n relationKeyPrefix: string;\n fieldPath: string[];\n parentReferenceName: string;\n parentTargetIdentifier?: string;\n}\n\nexport interface GenerateDrizzleSchemaOptions {\n /**\n * Module specifier to import the core schema tables (npUsers, npMedia) from.\n * Defaults to \"@nexpress/core\". Override when the consumer's tooling\n * (e.g. drizzle-kit's CJS resolver) can't load the core package via its\n * exports map — point at a relative path to core's source in that case.\n */\n schemaImport?: string;\n}\n\nexport function generateDrizzleSchema(\n collections: NpCollectionConfig[],\n options?: GenerateDrizzleSchemaOptions,\n): string {\n const schemaImport = options?.schemaImport ?? \"@nexpress/core\";\n const collectionTables = new Map<string, string>();\n\n for (const collection of collections) {\n collectionTables.set(collection.slug, getCollectionTableIdentifier(collection.slug));\n }\n\n const tables: GeneratedTable[] = [];\n\n for (const collection of collections) {\n const tableIdentifier = getCollectionTableIdentifier(collection.slug);\n const tableName = `np_c_${collection.slug}`;\n const columns = getBaseColumns(collection);\n const indexes = [`index(\"${tableName}_status_idx\").on(table.status)`];\n const relations: TableRelation[] = [\n {\n key: \"createdByUser\",\n kind: \"one\",\n targetIdentifier: \"npUsers\",\n fields: [\"createdBy\"],\n references: [\"id\"],\n },\n {\n key: \"updatedByUser\",\n kind: \"one\",\n targetIdentifier: \"npUsers\",\n fields: [\"updatedBy\"],\n references: [\"id\"],\n },\n ];\n\n // Phase 9.7b: collections that opt into member-write track the\n // member-author on the row itself so update/delete can perform\n // the owner check without a separate audit-log lookup. Nullable\n // because staff-authored docs in the same table leave it null;\n // the FK to np_members keeps the column safe under member\n // deletes (cascade).\n const memberAuthored = Boolean(collection.community?.memberWrite?.create);\n if (memberAuthored) {\n columns.push(\n 'memberAuthorId: uuid(\"member_author_id\").references(() => npMembers.id, { onDelete: \"set null\" })',\n );\n indexes.push(\n `index(\"${tableName}_member_author_idx\").on(table.memberAuthorId)`,\n );\n relations.push({\n key: \"memberAuthor\",\n kind: \"one\",\n targetIdentifier: \"npMembers\",\n fields: [\"memberAuthorId\"],\n references: [\"id\"],\n });\n }\n\n const scalarResult = collectScalarColumns(collection.fields, [], collectionTables);\n columns.push(...scalarResult.columns);\n relations.push(...scalarResult.relations);\n\n if (hasSlugField(collection)) {\n columns.push('slug: text(\"slug\").notNull()');\n // Phase 15.2 — multi-site scoping. Every collection\n // table gets a `site_id` column so the same slug can\n // exist in multiple sites (e.g., `/about` on the main\n // site and on `acme.example.com`). i18n collections\n // additionally key on locale, so the unique becomes\n // `(site_id, locale, slug)`; non-i18n becomes\n // `(site_id, slug)`. Single-tenant installs leave\n // every row at `site_id = 'default'`, so the\n // uniqueness constraint behaves identically to the\n // pre-15.2 `slug-only` index.\n if (collection.i18n) {\n indexes.push(\n `uniqueIndex(\"${tableName}_site_locale_slug_idx\").on(table.siteId, table.locale, table.slug)`,\n );\n } else {\n indexes.push(\n `uniqueIndex(\"${tableName}_site_slug_idx\").on(table.siteId, table.slug)`,\n );\n }\n }\n\n if (collection.i18n) {\n columns.push('locale: text(\"locale\").notNull()');\n columns.push('translationGroupId: uuid(\"translation_group_id\").notNull()');\n indexes.push(\n `index(\"${tableName}_translation_group_idx\").on(table.translationGroupId)`,\n );\n indexes.push(\n `index(\"${tableName}_locale_idx\").on(table.locale)`,\n );\n }\n\n // Phase 15.2 — multi-site scoping column. Default is\n // 'default' so existing single-tenant deployments keep\n // working without operator intervention; the migration\n // backfills existing rows. NOT NULL so writes always\n // commit a site id; pipeline reads `getCurrentSiteId()`\n // (or falls back to 'default') and stamps every row.\n // FK to `np_sites.id` is intentionally omitted from\n // codegen — adding it forces every test fixture to\n // create the default site row first; the framework\n // invariant (default site always exists post-migration)\n // gives us the same safety without the schema-level FK.\n columns.push(\n 'siteId: text(\"site_id\").default(\"default\").notNull()',\n );\n indexes.push(`index(\"${tableName}_site_idx\").on(table.siteId)`);\n\n if (hasDraftVersions(collection)) {\n columns.push(\n '_status: text(\"_status\", { enum: [\"draft\", \"published\"] }).default(\"draft\").notNull()',\n );\n }\n\n columns.push('searchVector: tsvector(\"search_vector\")');\n\n tables.push({\n identifier: tableIdentifier,\n tableName,\n columns,\n indexes,\n relations,\n });\n\n createNestedTables(\n {\n collectionSlug: collection.slug,\n tableIdentifier,\n tableName,\n relationKeyPrefix: toCamelCase(collection.slug),\n fieldPath: [],\n parentReferenceName: `${toCamelCase(collection.slug)}Id`,\n },\n collection.fields,\n tables,\n collectionTables,\n );\n }\n\n const body = tables.map(renderTable).join(\"\\n\\n\");\n const usesMembers = collections.some((c) => c.community?.memberWrite?.create);\n const schemaImports = [\"npMedia\", \"npUsers\", ...(usesMembers ? [\"npMembers\"] : [])];\n\n return [\n 'import { relations } from \"drizzle-orm\";',\n 'import { boolean, customType, doublePrecision, index, integer, jsonb, pgTable, text, timestamp, uniqueIndex, uuid } from \"drizzle-orm/pg-core\";',\n `import { ${schemaImports.join(\", \")} } from \"${schemaImport}\";`,\n '',\n 'const tsvector = customType<{ data: string }>({',\n ' dataType() {',\n ' return \"tsvector\";',\n ' },',\n '});',\n '',\n body,\n ].join(\"\\n\");\n}\n\nfunction createNestedTables(\n context: TableContext,\n fields: NpFieldConfig[],\n tables: GeneratedTable[],\n collectionTables: Map<string, string>,\n): void {\n for (const field of fields) {\n if (field.type === \"group\") {\n createNestedTables(context, field.fields, tables, collectionTables);\n continue;\n }\n\n if (field.type === \"row\" || field.type === \"collapsible\") {\n createNestedTables(context, field.fields, tables, collectionTables);\n continue;\n }\n\n if (field.type === \"array\") {\n tables.push(createArrayTable(context, field, tables, collectionTables));\n continue;\n }\n\n if (field.type === \"relationship\" && field.hasMany && typeof field.relationTo === \"string\") {\n tables.push(\n createHasManyJoinTable(context, { ...field, relationTo: field.relationTo, hasMany: true }, collectionTables),\n );\n }\n }\n}\n\nfunction createArrayTable(\n context: TableContext,\n field: NpArrayField,\n tables: GeneratedTable[],\n collectionTables: Map<string, string>,\n): GeneratedTable {\n const path = [...context.fieldPath, field.name];\n const tableName = `np_c_${context.collectionSlug}__${path.join(\"__\")}`;\n const identifier = getNestedTableIdentifier(context.collectionSlug, path);\n const columns = [\n 'id: uuid(\"id\").defaultRandom().primaryKey()',\n `parentId: uuid(\"parent_id\").notNull().references(() => ${context.tableIdentifier}.id, { onDelete: \"cascade\" })`,\n 'order: integer(\"order\").default(0).notNull()',\n ];\n const relations: TableRelation[] = [\n {\n key: \"parent\",\n kind: \"one\",\n targetIdentifier: context.tableIdentifier,\n fields: [\"parentId\"],\n references: [\"id\"],\n },\n ];\n const scalarResult = collectScalarColumns(field.fields, [], collectionTables);\n columns.push(...scalarResult.columns);\n relations.push(...scalarResult.relations);\n\n const nestedContext: TableContext = {\n collectionSlug: context.collectionSlug,\n tableIdentifier: identifier,\n tableName,\n relationKeyPrefix: `${context.relationKeyPrefix}${toPascalCase(field.name)}`,\n fieldPath: path,\n parentReferenceName: \"parentId\",\n parentTargetIdentifier: context.tableIdentifier,\n };\n\n createNestedTables(nestedContext, field.fields, tables, collectionTables);\n\n return {\n identifier,\n tableName,\n columns,\n indexes: [`index(\"${tableName}_parent_idx\").on(table.parentId)`],\n relations,\n };\n}\n\nfunction createHasManyJoinTable(\n context: TableContext,\n field: NpRelationshipField & { relationTo: string; hasMany: true },\n collectionTables: Map<string, string>,\n): GeneratedTable {\n const path = [...context.fieldPath, field.name];\n const tableName = `np_c_${context.collectionSlug}__${path.join(\"__\")}`;\n const identifier = getNestedTableIdentifier(context.collectionSlug, path);\n const targetIdentifier = resolveRelationTarget(field.relationTo, collectionTables);\n const parentReferenceName = context.fieldPath.length === 0 ? `${toCamelCase(context.collectionSlug)}Id` : \"parentId\";\n\n return {\n identifier,\n tableName,\n columns: [\n 'id: uuid(\"id\").defaultRandom().primaryKey()',\n `${parentReferenceName}: uuid(\"${toSnakeCase(parentReferenceName)}\").notNull().references(() => ${context.tableIdentifier}.id, { onDelete: \"cascade\" })`,\n `targetId: uuid(\"target_id\").notNull().references(() => ${targetIdentifier}.id, { onDelete: \"cascade\" })`,\n 'order: integer(\"order\").default(0).notNull()',\n ],\n indexes: [\n `index(\"${tableName}_${toSnakeCase(parentReferenceName)}_idx\").on(table.${parentReferenceName})`,\n `uniqueIndex(\"${tableName}_parent_target_uidx\").on(table.${parentReferenceName}, table.targetId)`,\n ],\n relations: [\n {\n key: \"parent\",\n kind: \"one\",\n targetIdentifier: context.tableIdentifier,\n fields: [parentReferenceName],\n references: [\"id\"],\n },\n {\n key: \"target\",\n kind: \"one\",\n targetIdentifier,\n fields: [\"targetId\"],\n references: [\"id\"],\n },\n ],\n };\n}\n\nfunction collectScalarColumns(\n fields: NpFieldConfig[],\n prefix: string[],\n collectionTables: Map<string, string>,\n): ScalarFieldResult {\n const columns: string[] = [];\n const relations: TableRelation[] = [];\n\n for (const field of fields) {\n if (field.type === \"group\") {\n const nested = collectScalarColumns(field.fields, [...prefix, field.name], collectionTables);\n columns.push(...nested.columns);\n relations.push(...nested.relations);\n continue;\n }\n\n if (field.type === \"row\" || field.type === \"collapsible\") {\n const nested = collectScalarColumns(field.fields, prefix, collectionTables);\n columns.push(...nested.columns);\n relations.push(...nested.relations);\n continue;\n }\n\n if (field.type === \"array\") {\n continue;\n }\n\n if (field.type === \"relationship\" && field.hasMany) {\n continue;\n }\n\n const propertyName = getFlattenedFieldName(prefix, field.name);\n const columnName = toSnakeCase(propertyName);\n const column = buildScalarColumn(field, propertyName, columnName, collectionTables);\n\n if (!column) {\n continue;\n }\n\n columns.push(...column.columns);\n relations.push(...column.relations);\n }\n\n return { columns, relations };\n}\n\nfunction buildScalarColumn(\n field: Exclude<NpFieldConfig, { type: \"row\" | \"collapsible\" | \"group\" | \"array\" }> ,\n propertyName: string,\n columnName: string,\n collectionTables: Map<string, string>,\n): ScalarFieldResult | null {\n const notNull = field.required ? \".notNull()\" : \"\";\n\n switch (field.type) {\n case \"text\":\n case \"textarea\":\n case \"email\":\n case \"select\":\n case \"radio\":\n return { columns: [`${propertyName}: text(\"${columnName}\")${notNull}`], relations: [] };\n case \"number\": {\n const builder = field.integerOnly ? \"integer\" : \"doublePrecision\";\n return { columns: [`${propertyName}: ${builder}(\"${columnName}\")${notNull}`], relations: [] };\n }\n case \"richText\":\n case \"blocks\":\n case \"json\":\n return { columns: [`${propertyName}: jsonb(\"${columnName}\")${notNull}`], relations: [] };\n case \"checkbox\":\n return { columns: [`${propertyName}: boolean(\"${columnName}\")${notNull}`], relations: [] };\n case \"date\":\n return {\n columns: [`${propertyName}: timestamp(\"${columnName}\", { withTimezone: true })${notNull}`],\n relations: [],\n };\n case \"upload\": {\n return {\n columns: [`${propertyName}: uuid(\"${columnName}\").references(() => npMedia.id)${notNull}`],\n relations: [\n {\n key: propertyName,\n kind: \"one\",\n targetIdentifier: \"npMedia\",\n fields: [propertyName],\n references: [\"id\"],\n },\n ],\n };\n }\n case \"relationship\": {\n if (typeof field.relationTo !== \"string\") {\n return null;\n }\n\n const targetIdentifier = resolveRelationTarget(field.relationTo, collectionTables);\n return {\n columns: [`${propertyName}: uuid(\"${columnName}\").references(() => ${targetIdentifier}.id)${notNull}`],\n relations: [\n {\n key: propertyName,\n kind: \"one\",\n targetIdentifier,\n fields: [propertyName],\n references: [\"id\"],\n },\n ],\n };\n }\n default:\n return null;\n }\n}\n\nfunction getBaseColumns(collection: NpCollectionConfig): string[] {\n const columns = ['id: uuid(\"id\").defaultRandom().primaryKey()'];\n\n columns.push(\n 'status: text(\"status\", { enum: [\"draft\", \"scheduled\", \"published\", \"archived\", \"pending\"] }).default(\"draft\").notNull()',\n );\n\n columns.push('createdAt: timestamp(\"created_at\", { withTimezone: true }).defaultNow().notNull()');\n columns.push('updatedAt: timestamp(\"updated_at\", { withTimezone: true }).defaultNow().notNull()');\n columns.push('createdBy: uuid(\"created_by\").references(() => npUsers.id)');\n columns.push('updatedBy: uuid(\"updated_by\").references(() => npUsers.id)');\n\n // Phase 21.17 — per-doc visibility flag. Orthogonal to\n // `status` (workflow state): a row can be published-public,\n // published-private, draft-public, etc. Anonymous reads in\n // `findDocuments` auto-filter to `visibility = \"public\"` so a\n // newly-imported \"private\" row never leaks to crawlers; an\n // authenticated principal (member or staff) sees both. WP\n // imports use this to round-trip `<wp:status>private</wp:status>`\n // posts as `status=published, visibility=private` rather than\n // the old draft-coercion that lost the publish state.\n const visibility =\n 'visibility: text(\"visibility\", { enum: [\"public\", \"private\"] }).default(\"public\").notNull()';\n\n if (collection.timestamps === false) {\n return [columns[0], columns[1], columns[4], columns[5], visibility];\n }\n\n columns.push(visibility);\n return columns;\n}\n\nfunction renderTable(table: GeneratedTable): string {\n const tableSource = [\n `export const ${table.identifier} = pgTable(`,\n ` \"${table.tableName}\",`,\n \" {\",\n ...table.columns.map((column) => ` ${column},`),\n \" },\",\n ` (table) => [${table.indexes.join(\", \")}],`,\n \");\",\n ].join(\"\\n\");\n\n const relationsSource = [\n `export const ${table.identifier}Relations = relations(${table.identifier}, ({ many, one }) => ({`,\n ...table.relations.map((relation) => renderRelation(relation, table.identifier)),\n \"}));\",\n ].join(\"\\n\");\n\n return `${tableSource}\\n\\n${relationsSource}`;\n}\n\nfunction renderRelation(relation: TableRelation, ownerIdentifier: string): string {\n if (relation.kind === \"many\") {\n return ` ${relation.key}: many(${relation.targetIdentifier}),`;\n }\n\n const fields = (relation.fields ?? [])\n .map((field) => `${ownerIdentifier}.${field}`)\n .join(\", \");\n const references = (relation.references ?? [])\n .map((reference) => `${relation.targetIdentifier}.${reference}`)\n .join(\", \");\n\n return ` ${relation.key}: one(${relation.targetIdentifier}, { fields: [${fields}], references: [${references}] }),`;\n}\n\nfunction hasSlugField(collection: NpCollectionConfig): boolean {\n return collection.slugField !== undefined && collection.slugField !== false;\n}\n\nfunction hasDraftVersions(collection: NpCollectionConfig): boolean {\n return Boolean(collection.versions?.drafts);\n}\n\nfunction resolveRelationTarget(\n relationTo: string,\n collectionTables: Map<string, string>,\n): string {\n if (relationTo === \"media\") {\n return \"npMedia\";\n }\n\n if (relationTo === \"users\") {\n return \"npUsers\";\n }\n\n return collectionTables.get(relationTo) ?? getCollectionTableIdentifier(relationTo);\n}\n\nfunction getCollectionTableIdentifier(slug: string): string {\n return `${toCamelCase(slug)}Table`;\n}\n\nfunction getNestedTableIdentifier(collectionSlug: string, path: string[]): string {\n return `${toCamelCase(collectionSlug)}${path.map(toPascalCase).join(\"\")}Table`;\n}\n\nfunction getFlattenedFieldName(prefix: string[], name: string): string {\n if (prefix.length === 0) {\n return toCamelCase(name);\n }\n\n return `${prefix.map(toPascalCase).join(\"\")}${toPascalCase(name)}`.replace(/^./u, (char) => char.toLowerCase());\n}\n\nfunction toCamelCase(value: string): string {\n const parts = splitName(value);\n const [first = \"\", ...rest] = parts;\n return `${first}${rest.map(capitalize).join(\"\")}`;\n}\n\nfunction toPascalCase(value: string): string {\n return splitName(value).map(capitalize).join(\"\");\n}\n\nfunction toSnakeCase(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, \"$1_$2\")\n .replace(/[^a-zA-Z0-9]+/g, \"_\")\n .toLowerCase();\n}\n\nfunction splitName(value: string): string[] {\n return value\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .split(/[^a-zA-Z0-9]+/)\n .map((part) => part.toLowerCase())\n .filter(Boolean);\n}\n\nfunction capitalize(value: string): string {\n return value.charAt(0).toUpperCase() + value.slice(1);\n}\n","import { type NpCollectionConfig, type NpFieldConfig } from \"../config/types.js\";\n\nexport function generateTypeScript(collections: NpCollectionConfig[]): string {\n const interfaces = collections.map((collection) => renderCollectionInterface(collection));\n return interfaces.join(\"\\n\\n\");\n}\n\ninterface HasManyDescriptor {\n /** Field name on the collection (the where clause key). */\n fieldName: string;\n /** Generated join-table import name (e.g., `postsCategoriesTable`). */\n joinTable: string;\n /** Parent FK column on the join table (e.g., `postsId`). */\n parentColumn: string;\n}\n\nfunction collectHasManyFields(collection: NpCollectionConfig): HasManyDescriptor[] {\n const collCamel = toCamelCase(collection.slug);\n const parentColumn = `${collCamel}Id`;\n const out: HasManyDescriptor[] = [];\n // Only top-level fields participate. Nested hasMany (inside row /\n // collapsible / group / array) is rare in practice and the join-\n // table naming convention doesn't carry through nesting cleanly.\n for (const field of collection.fields) {\n if (field.type === \"relationship\" && field.hasMany === true) {\n const joinTable = `${collCamel}${toPascalCase(field.name)}Table`;\n out.push({ fieldName: field.name, joinTable, parentColumn });\n }\n }\n return out;\n}\n\n/**\n * Renders a complete `documents.ts` module: imports from\n * `@nexpress/core`, per-collection `${Pascal}Document` interfaces,\n * and typed read-helper wrappers (`find${Pascal}` /\n * `get${Pascal}Document`) that bind the type generic so call sites\n * don't have to.\n *\n * Collections with hasMany relationship fields get a smarter\n * wrapper: when the caller's `where` clause names a hasMany\n * field, the wrapper queries the join table for matching parent\n * ids first, intersects across multiple hasMany filters, and\n * delegates to `findDocuments` with `id: idList`. That covers\n * the friction surfaced in #540's category-page dogfood — a\n * `where: { categories: id }` filter looked typed but failed at\n * runtime because `categories` isn't a column on the parent\n * table; this wrapper makes it work.\n *\n * The output is meant for `apps/<app>/src/db/generated/documents.ts`\n * and is a direct counterpart to `generateDrizzleSchema`'s\n * `collections.ts` (Drizzle schema). Both files come from the same\n * `nexpressConfig.collections` source so they stay in sync.\n */\nexport function generateDocumentsModule(collections: NpCollectionConfig[]): string {\n const hasManyByCollection = new Map(\n collections.map((c) => [c.slug, collectHasManyFields(c)]),\n );\n const anyHasMany = Array.from(hasManyByCollection.values()).some(\n (list) => list.length > 0,\n );\n\n const coreImports = [\n `import {`,\n ` findDocuments,`,\n ...(anyHasMany ? [` getDb,`] : []),\n ` getDocumentById,`,\n ` type NpAuthUser,`,\n ` type NpFindOptions,`,\n ` type NpFindResult,`,\n `} from \"@nexpress/core\";`,\n ].join(\"\\n\");\n\n // Drizzle + join-table imports only when at least one collection\n // has a hasMany relationship — keeps the file lean for sites\n // that don't use them.\n const drizzleImports = anyHasMany\n ? [\n `import { inArray } from \"drizzle-orm\";`,\n `import type { NodePgDatabase } from \"drizzle-orm/node-postgres\";`,\n ].join(\"\\n\")\n : \"\";\n const joinTables = Array.from(\n new Set(\n Array.from(hasManyByCollection.values())\n .flat()\n .map((d) => d.joinTable),\n ),\n ).sort();\n // Emit without the `.js` extension. The reference app's\n // `apps/web/tsconfig.json` uses `moduleResolution: \"Bundler\"`\n // (Next 16's standard); Bundler resolution + Turbopack don't\n // rewrite `.js` → `.ts` for relative imports the way tsc's\n // NodeNext resolution does, so the explicit extension breaks\n // `next build` even though `pnpm typecheck` passes (tsc handles\n // both shapes). Extension-less imports work under both\n // resolution strategies — Bundler resolves to the `.ts` file\n // directly, NodeNext does the same when the file extension\n // is omitted in TS source. See\n // https://github.com/vercel/next.js/issues for the long\n // history of `.js`-extension friction with Turbopack.\n const joinTableImports =\n joinTables.length > 0\n ? `import { ${joinTables.join(\", \")} } from \"./collections\";`\n : \"\";\n\n const imports = [coreImports, drizzleImports, joinTableImports]\n .filter(Boolean)\n .join(\"\\n\");\n\n const interfaces = collections.map((c) => renderCollectionInterface(c)).join(\"\\n\\n\");\n const helpers = collections\n .map((c) => renderReadHelpers(c, hasManyByCollection.get(c.slug) ?? []))\n .join(\"\\n\\n\");\n\n return [imports, \"\", interfaces, \"\", helpers, \"\"].join(\"\\n\");\n}\n\nfunction renderReadHelpers(\n collection: NpCollectionConfig,\n hasMany: HasManyDescriptor[],\n): string {\n const docType = `${toPascalCase(collection.slug)}Document`;\n const findFnName = `find${toPascalCase(collection.slug)}`;\n const getFnName = `get${toPascalCase(collection.slug)}Document`;\n const slug = JSON.stringify(collection.slug);\n\n const findFn =\n hasMany.length === 0\n ? renderSimpleFindFn(findFnName, slug, docType, collection.slug)\n : renderHasManyFindFn(findFnName, slug, docType, collection.slug, hasMany);\n\n return [\n findFn,\n \"\",\n `/** Typed by-id fetch for the \\`${collection.slug}\\` collection. */`,\n `export function ${getFnName}(`,\n ` id: string,`,\n ` user?: NpAuthUser,`,\n `): Promise<${docType} | null> {`,\n ` return getDocumentById<${docType}>(${slug}, id, user);`,\n `}`,\n ].join(\"\\n\");\n}\n\nfunction renderSimpleFindFn(\n findFnName: string,\n slug: string,\n docType: string,\n slugLabel: string,\n): string {\n return [\n `/** Typed listing query for the \\`${slugLabel}\\` collection. */`,\n `export function ${findFnName}(`,\n ` options: NpFindOptions<${docType}> = {},`,\n ` user?: NpAuthUser,`,\n `): Promise<NpFindResult<${docType}>> {`,\n ` return findDocuments<${docType}>(${slug}, options, user);`,\n `}`,\n ].join(\"\\n\");\n}\n\nfunction renderHasManyFindFn(\n findFnName: string,\n slug: string,\n docType: string,\n slugLabel: string,\n hasMany: HasManyDescriptor[],\n): string {\n // Build a literal array of `{ field, table, column }` for\n // runtime iteration. Keep it inline (instead of a loose const)\n // so the wrapper is one self-contained function — no helpers\n // bleed into consumer code completion.\n const descriptors = hasMany\n .map(\n (d) =>\n ` { field: ${JSON.stringify(d.fieldName)}, table: ${d.joinTable}, parent: ${d.joinTable}.${d.parentColumn} },`,\n )\n .join(\"\\n\");\n\n return [\n `/**`,\n ` * Typed listing query for the \\`${slugLabel}\\` collection.`,\n ` *`,\n ` * Pre-resolves hasMany relationship filters in the where`,\n ` * clause (${hasMany.map((d) => `\\`${d.fieldName}\\``).join(\", \")}) by`,\n ` * subquerying the join table for matching parent ids. Each`,\n ` * field accepts a single target id (most common) or an array`,\n ` * of target ids (OR semantics). Multiple hasMany filters`,\n ` * intersect — \\`where: { categories: catId, tags: tagId }\\``,\n ` * matches rows that have BOTH.`,\n ` */`,\n `export async function ${findFnName}(`,\n ` options: NpFindOptions<${docType}> = {},`,\n ` user?: NpAuthUser,`,\n `): Promise<NpFindResult<${docType}>> {`,\n ` const where = options.where ? { ...options.where } : {};`,\n ` const hasManyDescriptors = [`,\n descriptors,\n ` ];`,\n ``,\n ` const matched: string[][] = [];`,\n ` let touchedHasMany = false;`,\n ` for (const { field, table, parent } of hasManyDescriptors) {`,\n ` const value = (where as Record<string, unknown>)[field];`,\n ` if (value === undefined) continue;`,\n ` touchedHasMany = true;`,\n ` delete (where as Record<string, unknown>)[field];`,\n ` const targets = (Array.isArray(value) ? value : [value]).filter(`,\n ` (v): v is string => typeof v === \"string\" && v.length > 0,`,\n ` );`,\n ` if (targets.length === 0) {`,\n ` // Empty array short-circuits to no rows — match the`,\n ` // pipeline's array-where semantics.`,\n ` matched.push([]);`,\n ` continue;`,\n ` }`,\n ` // Cast getDb() to NodePgDatabase so the drizzle builder`,\n ` // chain (.select.from.where) carries proper return types.`,\n ` // The empty-schema generic narrows the return shape away`,\n ` // from any specific tables; the explicit \\`{ id: string }[]\\` `,\n ` // cast at the end matches the projection.`,\n ` const db = getDb() as unknown as NodePgDatabase<Record<string, never>>;`,\n ` const rows = (await db`,\n ` .select({ id: parent })`,\n ` .from(table)`,\n ` .where(inArray(table.targetId, targets))) as Array<{ id: string }>;`,\n ` matched.push(rows.map((r) => r.id));`,\n ` }`,\n ``,\n ` if (touchedHasMany) {`,\n ` // Intersect across all hasMany filters. Empty intersection`,\n ` // → return immediately; findDocuments would short-circuit`,\n ` // on the empty-array where clause anyway, but the early`,\n ` // exit saves a round-trip.`,\n ` let ids = matched[0] ?? [];`,\n ` for (let i = 1; i < matched.length; i++) {`,\n ` const set = new Set(matched[i]);`,\n ` ids = ids.filter((id) => set.has(id));`,\n ` }`,\n ``,\n ` // Honor any pre-existing user id constraint. Without this,`,\n ` // \\`where: { id: someId, categories: catId }\\` would silently`,\n ` // drop the user's id filter and return every post in that`,\n ` // category — a real foot-gun. Intersect instead.`,\n ` const existingId = (where as Record<string, unknown>).id;`,\n ` if (typeof existingId === \"string\") {`,\n ` ids = ids.includes(existingId) ? [existingId] : [];`,\n ` } else if (Array.isArray(existingId)) {`,\n ` const allowed = new Set(`,\n ` existingId.filter((v): v is string => typeof v === \"string\"),`,\n ` );`,\n ` ids = ids.filter((id) => allowed.has(id));`,\n ` }`,\n ``,\n ` if (ids.length === 0) {`,\n ` return {`,\n ` docs: [],`,\n ` totalDocs: 0,`,\n ` totalPages: 0,`,\n ` page: options.page ?? 1,`,\n ` limit: options.limit ?? 20,`,\n ` hasNextPage: false,`,\n ` hasPrevPage: false,`,\n ` };`,\n ` }`,\n ` (where as Record<string, unknown>).id = ids;`,\n ` }`,\n ``,\n ` return findDocuments<${docType}>(${slug}, { ...options, where }, user);`,\n `}`,\n ].join(\"\\n\");\n}\n\nfunction renderCollectionInterface(collection: NpCollectionConfig): string {\n const interfaceName = `${toPascalCase(collection.slug)}Document`;\n const fields = [\n 'id: string;',\n 'status: \"draft\" | \"published\" | \"archived\" | \"pending\";',\n 'createdAt: Date;',\n 'updatedAt: Date;',\n 'createdBy: string | null;',\n 'updatedBy: string | null;',\n ];\n\n if (collection.community?.memberWrite?.create) {\n fields.push('memberAuthorId: string | null;');\n }\n\n if (collection.slugField) {\n fields.push(\"slug: string;\");\n }\n\n if (collection.versions?.drafts) {\n fields.push('_status: \"draft\" | \"published\";');\n }\n\n fields.push(...renderFields(collection.fields));\n\n return [`export interface ${interfaceName} {`, ...fields.map((field) => ` ${field}`), \"}\"].join(\"\\n\");\n}\n\nfunction renderFields(fields: NpFieldConfig[], prefix: string[] = []): string[] {\n const lines: string[] = [];\n\n for (const field of fields) {\n if (field.type === \"row\" || field.type === \"collapsible\") {\n lines.push(...renderFields(field.fields, prefix));\n continue;\n }\n\n const fieldName = field.type === \"group\" ? getPropertyName(prefix, field.name) : \"\";\n\n if (field.type === \"group\") {\n const groupType = renderObjectType(field.fields);\n lines.push(`${fieldName}: ${applyNullability(groupType, field.required)};`);\n continue;\n }\n\n const propertyName = getPropertyName(prefix, field.name);\n const typeSource = getTypeSource(field);\n lines.push(`${propertyName}: ${applyNullability(typeSource, field.required)};`);\n }\n\n return lines;\n}\n\nfunction renderObjectType(fields: NpFieldConfig[]): string {\n const members = renderFields(fields).map((field) => ` ${field}`);\n return [`{`, ...members, `}`].join(\"\\n\");\n}\n\nfunction getTypeSource(field: Exclude<NpFieldConfig, { type: \"row\" | \"collapsible\" | \"group\" }>): string {\n switch (field.type) {\n case \"text\":\n case \"textarea\":\n case \"email\":\n case \"select\":\n case \"radio\":\n return \"string\";\n case \"number\":\n return \"number\";\n case \"checkbox\":\n return \"boolean\";\n case \"date\":\n return \"Date\";\n case \"upload\":\n return \"string\";\n case \"relationship\":\n return field.hasMany ? \"string[]\" : \"string\";\n case \"array\":\n return `Array<${renderObjectType(field.fields)}>`;\n case \"richText\":\n case \"blocks\":\n case \"json\":\n return \"unknown\";\n default:\n return \"unknown\";\n }\n}\n\nfunction applyNullability(typeSource: string, required?: boolean): string {\n return required ? typeSource : `${typeSource} | null`;\n}\n\nfunction getPropertyName(prefix: string[], name: string): string {\n if (prefix.length === 0) {\n return toCamelCase(name);\n }\n\n return `${prefix[0]}${prefix.slice(1).map(toPascalCase).join(\"\")}${toPascalCase(name)}`;\n}\n\nfunction toCamelCase(value: string): string {\n const parts = splitName(value);\n const [first = \"\", ...rest] = parts;\n return `${first}${rest.map(toPascalCase).join(\"\")}`;\n}\n\nfunction toPascalCase(value: string): string {\n return splitName(value)\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join(\"\");\n}\n\nfunction splitName(value: string): string[] {\n return value\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .split(/[^a-zA-Z0-9]+/)\n .map((part) => part.toLowerCase())\n .filter(Boolean);\n}\n"],"mappings":"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","names":["toCamelCase","toPascalCase","splitName"]}
|