@payload-cc/payload-collection-cli 1.1.0 → 1.3.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/README.md +21 -29
- package/dist/bin.cjs +236 -0
- package/dist/bin.js +76 -162
- package/dist/{chunk-I7HHN7EL.mjs → chunk-D5RS7XAO.js} +16 -10
- package/dist/index.cjs +150 -0
- package/dist/{index.d.mts → index.d.cts} +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +6 -134
- package/package.json +11 -3
- package/dist/bin.mjs +0 -63
- package/dist/index.mjs +0 -8
- /package/dist/{bin.d.mts → bin.d.cts} +0 -0
package/README.md
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
# payload-collection-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The missing CLI for Payload 3.0. Manage collections without writing boilerplate scripts.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Why?
|
|
8
|
+
|
|
9
|
+
Payload 3.0 is powerful, but performing simple data operations often requires writing one-off scripts. **payload-collection-cli** turns your terminal into a direct interface for your collections, supporting intelligent relation lookups by name/slug instead of cryptic IDs.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
- ⚡️ **Zero Scripting:** Run CRUD operations directly from your terminal.
|
|
14
|
+
- 🔍 **Relation Lookup:** Resolve relationships using `name`, `email`, or `slug`.
|
|
15
|
+
- 📄 **Batch Processing:** Support for both JSON strings and JSONLines (`.jsonl`) files.
|
|
16
|
+
- ⚙️ **Configurable:** Define your lookup logic in a simple external config.
|
|
17
|
+
- 🛡 **Native Performance:** Uses Local API to ensure all Hooks and Validations run.
|
|
18
|
+
|
|
19
|
+
## 📖 Documentation
|
|
20
|
+
|
|
21
|
+
- **🚀 [Official Guide](https://payload-cc.github.io/payload-collection-cli/)**
|
|
22
|
+
- **🤖 [AI Context Page](https://payload-cc.github.io/payload-collection-cli/ai.html)** (One-file summary for coding agents)
|
|
6
23
|
|
|
7
24
|
## Installation
|
|
8
25
|
|
|
@@ -17,7 +34,7 @@ You can immediately start using the commands without any configuration for basic
|
|
|
17
34
|
|
|
18
35
|
**Command Syntax**:
|
|
19
36
|
```bash
|
|
20
|
-
npx @payload-cc/payload-collection-cli [
|
|
37
|
+
npx @payload-cc/payload-collection-cli [options] <collection-slug> <operation> <file or string>
|
|
21
38
|
```
|
|
22
39
|
|
|
23
40
|
**Examples**:
|
|
@@ -39,33 +56,8 @@ npx @payload-cc/payload-collection-cli -c ./my-map.config.ts users upsert data.j
|
|
|
39
56
|
- `upsert`: Update if existing, create if not.
|
|
40
57
|
|
|
41
58
|
## Specifications & FAQ
|
|
42
|
-
For detailed behavior specifications (such as how identifier lookups work strictly during upserts, updates, and deletes), please refer to the [Specifications & FAQ](docs/references/specs_detail.md).
|
|
43
|
-
|
|
44
|
-
## Configuration (Optional)
|
|
45
|
-
|
|
46
|
-
### Relation Mappings (`payload-collection-cli.config.ts`)
|
|
47
|
-
By default, Payload relations require you to provide target document IDs (e.g., ObjectIDs or numeric IDs). The CLI can magically resolve these relations by searching for human-readable fields instead.
|
|
48
|
-
|
|
49
|
-
**Example Scenario**:
|
|
50
|
-
Assume your `posts` collection has a relationship field named `author` that references the `users` collection. Instead of manually finding and hard-coding the user's database ID, you want to simply provide their email address.
|
|
51
|
-
|
|
52
|
-
Create a `payload-collection-cli.config.ts` in your Payload project root (or pass it via `-c`) to define the `lookupField` for the `users` collection:
|
|
53
|
-
|
|
54
|
-
```typescript
|
|
55
|
-
export const cliConfig = {
|
|
56
|
-
mappings: {
|
|
57
|
-
users: {
|
|
58
|
-
lookupField: 'email',
|
|
59
|
-
onNotFound: 'error',
|
|
60
|
-
},
|
|
61
|
-
posts: {
|
|
62
|
-
lookupField: 'slug'
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
59
|
|
|
68
|
-
|
|
60
|
+
For detailed configuration options (including `package.json` defaults, relation mappings, `onNotFound` behaviors, and cross-file imports), please refer to the [Specifications & FAQ](docs/references/specs_detail.md).
|
|
69
61
|
|
|
70
62
|
## Development & CI/CD
|
|
71
63
|
|
package/dist/bin.cjs
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
|
|
27
|
+
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
28
|
+
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
29
|
+
|
|
30
|
+
// src/bin.ts
|
|
31
|
+
var import_payload = require("payload");
|
|
32
|
+
var import_jiti = require("jiti");
|
|
33
|
+
var import_path2 = __toESM(require("path"), 1);
|
|
34
|
+
var import_fs2 = __toESM(require("fs"), 1);
|
|
35
|
+
var import_yargs = __toESM(require("yargs/yargs"), 1);
|
|
36
|
+
var import_helpers = require("yargs/helpers");
|
|
37
|
+
var import_zod = require("zod");
|
|
38
|
+
|
|
39
|
+
// src/executor.ts
|
|
40
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
41
|
+
var import_readline = __toESM(require("readline"), 1);
|
|
42
|
+
var import_path = __toESM(require("path"), 1);
|
|
43
|
+
|
|
44
|
+
// src/resolver.ts
|
|
45
|
+
async function resolveRelations(payload, collectionSlug, data, config) {
|
|
46
|
+
const collection = payload.collections[collectionSlug];
|
|
47
|
+
if (!collection) throw new Error(`Collection "${collectionSlug}" not found.`);
|
|
48
|
+
const resolved = { ...data };
|
|
49
|
+
const mappingConfig = config.mappings[collectionSlug];
|
|
50
|
+
if (mappingConfig?.defaults) {
|
|
51
|
+
for (const [k, v] of Object.entries(mappingConfig.defaults)) {
|
|
52
|
+
if (resolved[k] === void 0 || resolved[k] === null) {
|
|
53
|
+
resolved[k] = v;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const field of collection.config.fields) {
|
|
58
|
+
if (field.type === "relationship" && resolved[field.name]) {
|
|
59
|
+
const relationTo = Array.isArray(field.relationTo) ? field.relationTo[0] : field.relationTo;
|
|
60
|
+
const mapping = config.mappings[relationTo];
|
|
61
|
+
if (!mapping) continue;
|
|
62
|
+
const rawValue = resolved[field.name];
|
|
63
|
+
const isArray = Array.isArray(rawValue);
|
|
64
|
+
const values = isArray ? rawValue : [rawValue];
|
|
65
|
+
const resolvedIds = [];
|
|
66
|
+
for (const val of values) {
|
|
67
|
+
if (typeof val === "string" && val.length < 24) {
|
|
68
|
+
const found = await payload.find({
|
|
69
|
+
collection: relationTo,
|
|
70
|
+
where: { [mapping.lookupField]: { equals: val } },
|
|
71
|
+
limit: 1
|
|
72
|
+
});
|
|
73
|
+
if (found.docs.length > 0) {
|
|
74
|
+
resolvedIds.push(found.docs[0].id);
|
|
75
|
+
} else if (mapping.onNotFound === "create") {
|
|
76
|
+
const created = await payload.create({
|
|
77
|
+
collection: relationTo,
|
|
78
|
+
data: { [mapping.lookupField]: val }
|
|
79
|
+
});
|
|
80
|
+
resolvedIds.push(created.id);
|
|
81
|
+
} else if (mapping.onNotFound === "error") {
|
|
82
|
+
throw new Error(`Relation not found: ${relationTo} (${mapping.lookupField}=${val})`);
|
|
83
|
+
} else {
|
|
84
|
+
resolvedIds.push(val);
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
resolvedIds.push(val);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
resolved[field.name] = isArray ? resolvedIds : resolvedIds[0];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return resolved;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/executor.ts
|
|
97
|
+
async function processSingle(payload, collection, action, data, config) {
|
|
98
|
+
const mapping = config.mappings[collection];
|
|
99
|
+
const lookupField = mapping?.lookupField || "id";
|
|
100
|
+
const resolved = await resolveRelations(payload, collection, data, config);
|
|
101
|
+
switch (action) {
|
|
102
|
+
case "create":
|
|
103
|
+
return await payload.create({ collection, data: resolved });
|
|
104
|
+
case "upsert":
|
|
105
|
+
if (data[lookupField] === void 0) {
|
|
106
|
+
throw new Error(`[upsert] Missing lookup field '${lookupField}' in data. Cannot perform upsert.`);
|
|
107
|
+
}
|
|
108
|
+
const existingUpsert = await payload.find({
|
|
109
|
+
collection,
|
|
110
|
+
where: { [lookupField]: { equals: data[lookupField] } }
|
|
111
|
+
});
|
|
112
|
+
if (existingUpsert.docs.length > 0) {
|
|
113
|
+
return await payload.update({ collection, id: existingUpsert.docs[0].id, data: resolved });
|
|
114
|
+
}
|
|
115
|
+
return await payload.create({ collection, data: resolved });
|
|
116
|
+
case "update":
|
|
117
|
+
if (data[lookupField] === void 0) {
|
|
118
|
+
throw new Error(`[update] Missing lookup field '${lookupField}' in data. Cannot perform update.`);
|
|
119
|
+
}
|
|
120
|
+
return await payload.update({
|
|
121
|
+
collection,
|
|
122
|
+
where: { [lookupField]: { equals: data[lookupField] } },
|
|
123
|
+
data: resolved
|
|
124
|
+
});
|
|
125
|
+
case "delete":
|
|
126
|
+
const delVal = typeof data === "object" ? data[lookupField] : data;
|
|
127
|
+
if (delVal === void 0) {
|
|
128
|
+
throw new Error(`[delete] Missing lookup field '${lookupField}' in action. Cannot perform delete.`);
|
|
129
|
+
}
|
|
130
|
+
return await payload.delete({
|
|
131
|
+
collection,
|
|
132
|
+
where: { [lookupField]: { equals: delVal } }
|
|
133
|
+
});
|
|
134
|
+
default:
|
|
135
|
+
throw new Error(`Unsupported action: ${action}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function execute(payload, collection, action, input, config) {
|
|
139
|
+
if (input.endsWith(".jsonl")) {
|
|
140
|
+
const filePath = import_path.default.resolve(process.cwd(), input);
|
|
141
|
+
const rl = import_readline.default.createInterface({ input: import_fs.default.createReadStream(filePath) });
|
|
142
|
+
for await (const line of rl) {
|
|
143
|
+
if (line.trim()) await processSingle(payload, collection, action, JSON.parse(line), config);
|
|
144
|
+
}
|
|
145
|
+
return { status: "bulk success" };
|
|
146
|
+
}
|
|
147
|
+
return await processSingle(payload, collection, action, JSON.parse(input), config);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/bin.ts
|
|
151
|
+
var jiti = (0, import_jiti.createJiti)(importMetaUrl, { moduleCache: false, fsCache: false });
|
|
152
|
+
var MappingConfigSchema = import_zod.z.object({
|
|
153
|
+
lookupField: import_zod.z.string().default("id"),
|
|
154
|
+
onNotFound: import_zod.z.enum(["error", "ignore", "create"]).default("error"),
|
|
155
|
+
defaults: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional()
|
|
156
|
+
});
|
|
157
|
+
var CLIConfigSchema = import_zod.z.object({
|
|
158
|
+
mappings: import_zod.z.record(import_zod.z.string(), MappingConfigSchema).default({})
|
|
159
|
+
});
|
|
160
|
+
async function run() {
|
|
161
|
+
console.log("\u{1F3C1} Starting CLI...");
|
|
162
|
+
const root = process.cwd();
|
|
163
|
+
const argv = await (0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).usage("Usage: $0 [options] <collection-slug> <operation> <file or string>").options({
|
|
164
|
+
"config-file": {
|
|
165
|
+
alias: "c",
|
|
166
|
+
type: "string",
|
|
167
|
+
describe: "Path to a configuration file (named export required)."
|
|
168
|
+
},
|
|
169
|
+
"config-json": {
|
|
170
|
+
alias: "j",
|
|
171
|
+
type: "string",
|
|
172
|
+
describe: "Inline JSON string for configuration."
|
|
173
|
+
},
|
|
174
|
+
"config-export-name": {
|
|
175
|
+
alias: "n",
|
|
176
|
+
type: "string",
|
|
177
|
+
describe: "The name of the export to use from the configuration file.",
|
|
178
|
+
default: "cliConfig"
|
|
179
|
+
}
|
|
180
|
+
}).pkgConf("payload-collection-cli").demandCommand(3, "Collection slug, operation, and input are required.").help().parse();
|
|
181
|
+
const { configFile, configJson, configExportName, _: [collection, action, input] } = argv;
|
|
182
|
+
let rawConfig = { mappings: {} };
|
|
183
|
+
if (configJson) {
|
|
184
|
+
try {
|
|
185
|
+
rawConfig = typeof configJson === "string" ? JSON.parse(configJson) : configJson;
|
|
186
|
+
} catch (err) {
|
|
187
|
+
console.error("\u274C Error: Failed to parse inline JSON config:", err);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
} else if (configFile) {
|
|
191
|
+
const customConfigPath = import_path2.default.resolve(root, configFile);
|
|
192
|
+
if (!import_fs2.default.existsSync(customConfigPath)) {
|
|
193
|
+
console.error(`\u274C Error: Config file not found at ${customConfigPath}`);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
const imported = await jiti.import(customConfigPath);
|
|
197
|
+
if (imported[configExportName]) {
|
|
198
|
+
rawConfig = imported[configExportName];
|
|
199
|
+
} else {
|
|
200
|
+
console.error(`\u274C Error: Named export "${configExportName}" not found in ${configFile}`);
|
|
201
|
+
process.exit(1);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const validation = CLIConfigSchema.safeParse(rawConfig);
|
|
205
|
+
if (!validation.success) {
|
|
206
|
+
console.error("\u274C Error: Invalid configuration structure:");
|
|
207
|
+
validation.error.issues.forEach((issue) => {
|
|
208
|
+
console.error(` - [${issue.path.join(".")}] ${issue.message}`);
|
|
209
|
+
});
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
const cliConfig = validation.data;
|
|
213
|
+
const configPath = [
|
|
214
|
+
import_path2.default.resolve(root, "src/payload.config.ts"),
|
|
215
|
+
import_path2.default.resolve(root, "payload.config.ts")
|
|
216
|
+
].find((p) => import_fs2.default.existsSync(p));
|
|
217
|
+
if (!configPath) {
|
|
218
|
+
console.error("\u274C Error: payload.config.ts not found.");
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
const { default: payloadConfig } = await jiti.import(configPath);
|
|
222
|
+
const payload = await (0, import_payload.getPayload)({ config: payloadConfig });
|
|
223
|
+
console.log("\u2705 Connected to Payload");
|
|
224
|
+
try {
|
|
225
|
+
const result = await execute(payload, collection, action, input, cliConfig);
|
|
226
|
+
console.log("\u2728 Operation successful");
|
|
227
|
+
} catch (err) {
|
|
228
|
+
console.error("\u274C Error:", err.message);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
process.exit(0);
|
|
232
|
+
}
|
|
233
|
+
run().catch((err) => {
|
|
234
|
+
console.error("\u274C Fatal Error:", err.message);
|
|
235
|
+
process.exit(1);
|
|
236
|
+
});
|
package/dist/bin.js
CHANGED
|
@@ -1,178 +1,89 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
-
mod
|
|
24
|
-
));
|
|
25
|
-
|
|
26
|
-
// node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_tsx@4.21.0_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
|
|
27
|
-
var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
|
|
28
|
-
var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
|
|
2
|
+
import {
|
|
3
|
+
execute
|
|
4
|
+
} from "./chunk-D5RS7XAO.js";
|
|
29
5
|
|
|
30
6
|
// src/bin.ts
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
resolvedIds.push(val);
|
|
67
|
-
}
|
|
68
|
-
} else {
|
|
69
|
-
resolvedIds.push(val);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
resolved[field.name] = isArray ? resolvedIds : resolvedIds[0];
|
|
7
|
+
import { getPayload } from "payload";
|
|
8
|
+
import { createJiti } from "jiti";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
import yargs from "yargs/yargs";
|
|
12
|
+
import { hideBin } from "yargs/helpers";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
var jiti = createJiti(import.meta.url, { moduleCache: false, fsCache: false });
|
|
15
|
+
var MappingConfigSchema = z.object({
|
|
16
|
+
lookupField: z.string().default("id"),
|
|
17
|
+
onNotFound: z.enum(["error", "ignore", "create"]).default("error"),
|
|
18
|
+
defaults: z.record(z.string(), z.any()).optional()
|
|
19
|
+
});
|
|
20
|
+
var CLIConfigSchema = z.object({
|
|
21
|
+
mappings: z.record(z.string(), MappingConfigSchema).default({})
|
|
22
|
+
});
|
|
23
|
+
async function run() {
|
|
24
|
+
console.log("\u{1F3C1} Starting CLI...");
|
|
25
|
+
const root = process.cwd();
|
|
26
|
+
const argv = await yargs(hideBin(process.argv)).usage("Usage: $0 [options] <collection-slug> <operation> <file or string>").options({
|
|
27
|
+
"config-file": {
|
|
28
|
+
alias: "c",
|
|
29
|
+
type: "string",
|
|
30
|
+
describe: "Path to a configuration file (named export required)."
|
|
31
|
+
},
|
|
32
|
+
"config-json": {
|
|
33
|
+
alias: "j",
|
|
34
|
+
type: "string",
|
|
35
|
+
describe: "Inline JSON string for configuration."
|
|
36
|
+
},
|
|
37
|
+
"config-export-name": {
|
|
38
|
+
alias: "n",
|
|
39
|
+
type: "string",
|
|
40
|
+
describe: "The name of the export to use from the configuration file.",
|
|
41
|
+
default: "cliConfig"
|
|
73
42
|
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
switch (action) {
|
|
84
|
-
case "create":
|
|
85
|
-
return await payload.create({ collection, data: resolved });
|
|
86
|
-
case "upsert":
|
|
87
|
-
if (data[lookupField] === void 0) {
|
|
88
|
-
throw new Error(`[upsert] Missing lookup field '${lookupField}' in data. Cannot perform upsert.`);
|
|
89
|
-
}
|
|
90
|
-
const existingUpsert = await payload.find({
|
|
91
|
-
collection,
|
|
92
|
-
where: { [lookupField]: { equals: data[lookupField] } }
|
|
93
|
-
});
|
|
94
|
-
if (existingUpsert.docs.length > 0) {
|
|
95
|
-
return await payload.update({ collection, id: existingUpsert.docs[0].id, data: resolved });
|
|
96
|
-
}
|
|
97
|
-
return await payload.create({ collection, data: resolved });
|
|
98
|
-
case "update":
|
|
99
|
-
if (data[lookupField] === void 0) {
|
|
100
|
-
throw new Error(`[update] Missing lookup field '${lookupField}' in data. Cannot perform update.`);
|
|
101
|
-
}
|
|
102
|
-
return await payload.update({
|
|
103
|
-
collection,
|
|
104
|
-
where: { [lookupField]: { equals: data[lookupField] } },
|
|
105
|
-
data: resolved
|
|
106
|
-
});
|
|
107
|
-
case "delete":
|
|
108
|
-
const delVal = typeof data === "object" ? data[lookupField] : data;
|
|
109
|
-
if (delVal === void 0) {
|
|
110
|
-
throw new Error(`[delete] Missing lookup field '${lookupField}' in action. Cannot perform delete.`);
|
|
111
|
-
}
|
|
112
|
-
return await payload.delete({
|
|
113
|
-
collection,
|
|
114
|
-
where: { [lookupField]: { equals: delVal } }
|
|
115
|
-
});
|
|
116
|
-
default:
|
|
117
|
-
throw new Error(`Unsupported action: ${action}`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
async function execute(payload, collection, action, input, config) {
|
|
121
|
-
if (input.endsWith(".jsonl")) {
|
|
122
|
-
const filePath = import_path.default.resolve(process.cwd(), input);
|
|
123
|
-
const rl = import_readline.default.createInterface({ input: import_fs.default.createReadStream(filePath) });
|
|
124
|
-
for await (const line of rl) {
|
|
125
|
-
if (line.trim()) await processSingle(payload, collection, action, JSON.parse(line), config);
|
|
43
|
+
}).pkgConf("payload-collection-cli").demandCommand(3, "Collection slug, operation, and input are required.").help().parse();
|
|
44
|
+
const { configFile, configJson, configExportName, _: [collection, action, input] } = argv;
|
|
45
|
+
let rawConfig = { mappings: {} };
|
|
46
|
+
if (configJson) {
|
|
47
|
+
try {
|
|
48
|
+
rawConfig = typeof configJson === "string" ? JSON.parse(configJson) : configJson;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error("\u274C Error: Failed to parse inline JSON config:", err);
|
|
51
|
+
process.exit(1);
|
|
126
52
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// src/bin.ts
|
|
133
|
-
var jiti = (0, import_jiti.createJiti)(importMetaUrl);
|
|
134
|
-
async function run() {
|
|
135
|
-
const args = process.argv.slice(2);
|
|
136
|
-
let configOptIdx = args.indexOf("-c");
|
|
137
|
-
if (configOptIdx === -1) configOptIdx = args.indexOf("--config");
|
|
138
|
-
let cliConfig = { mappings: {} };
|
|
139
|
-
if (configOptIdx !== -1) {
|
|
140
|
-
if (args.length <= configOptIdx + 1) {
|
|
141
|
-
console.error("\u274C Error: Missing option after --config parameter.");
|
|
53
|
+
} else if (configFile) {
|
|
54
|
+
const customConfigPath = path.resolve(root, configFile);
|
|
55
|
+
if (!fs.existsSync(customConfigPath)) {
|
|
56
|
+
console.error(`\u274C Error: Config file not found at ${customConfigPath}`);
|
|
142
57
|
process.exit(1);
|
|
143
58
|
}
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
cliConfig = JSON.parse(val);
|
|
149
|
-
} catch (err) {
|
|
150
|
-
console.error("\u274C Error: Failed to parse inline JSON config:", err);
|
|
151
|
-
process.exit(1);
|
|
152
|
-
}
|
|
59
|
+
const imported = await jiti.import(customConfigPath);
|
|
60
|
+
if (imported[configExportName]) {
|
|
61
|
+
rawConfig = imported[configExportName];
|
|
153
62
|
} else {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
console.error(`\u274C Error: Config file not found at ${customConfigPath}`);
|
|
157
|
-
process.exit(1);
|
|
158
|
-
}
|
|
159
|
-
const imported = await jiti.import(customConfigPath);
|
|
160
|
-
cliConfig = imported.cliConfig || imported.default || cliConfig;
|
|
63
|
+
console.error(`\u274C Error: Named export "${configExportName}" not found in ${configFile}`);
|
|
64
|
+
process.exit(1);
|
|
161
65
|
}
|
|
162
66
|
}
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
67
|
+
const validation = CLIConfigSchema.safeParse(rawConfig);
|
|
68
|
+
if (!validation.success) {
|
|
69
|
+
console.error("\u274C Error: Invalid configuration structure:");
|
|
70
|
+
validation.error.issues.forEach((issue) => {
|
|
71
|
+
console.error(` - [${issue.path.join(".")}] ${issue.message}`);
|
|
72
|
+
});
|
|
167
73
|
process.exit(1);
|
|
168
74
|
}
|
|
75
|
+
const cliConfig = validation.data;
|
|
169
76
|
const configPath = [
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
].find((p) =>
|
|
173
|
-
if (!configPath)
|
|
77
|
+
path.resolve(root, "src/payload.config.ts"),
|
|
78
|
+
path.resolve(root, "payload.config.ts")
|
|
79
|
+
].find((p) => fs.existsSync(p));
|
|
80
|
+
if (!configPath) {
|
|
81
|
+
console.error("\u274C Error: payload.config.ts not found.");
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
174
84
|
const { default: payloadConfig } = await jiti.import(configPath);
|
|
175
|
-
const payload = await
|
|
85
|
+
const payload = await getPayload({ config: payloadConfig });
|
|
86
|
+
console.log("\u2705 Connected to Payload");
|
|
176
87
|
try {
|
|
177
88
|
const result = await execute(payload, collection, action, input, cliConfig);
|
|
178
89
|
console.log("\u2728 Operation successful");
|
|
@@ -182,4 +93,7 @@ async function run() {
|
|
|
182
93
|
}
|
|
183
94
|
process.exit(0);
|
|
184
95
|
}
|
|
185
|
-
run()
|
|
96
|
+
run().catch((err) => {
|
|
97
|
+
console.error("\u274C Fatal Error:", err.message);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
});
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/resolver.ts
|
|
9
2
|
async function resolveRelations(payload, collectionSlug, data, config) {
|
|
10
3
|
const collection = payload.collections[collectionSlug];
|
|
11
4
|
if (!collection) throw new Error(`Collection "${collectionSlug}" not found.`);
|
|
12
5
|
const resolved = { ...data };
|
|
6
|
+
const mappingConfig = config.mappings[collectionSlug];
|
|
7
|
+
if (mappingConfig?.defaults) {
|
|
8
|
+
for (const [k, v] of Object.entries(mappingConfig.defaults)) {
|
|
9
|
+
if (resolved[k] === void 0 || resolved[k] === null) {
|
|
10
|
+
resolved[k] = v;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
13
14
|
for (const field of collection.config.fields) {
|
|
14
|
-
if (field.type === "relationship" &&
|
|
15
|
+
if (field.type === "relationship" && resolved[field.name]) {
|
|
15
16
|
const relationTo = Array.isArray(field.relationTo) ? field.relationTo[0] : field.relationTo;
|
|
16
17
|
const mapping = config.mappings[relationTo];
|
|
17
18
|
if (!mapping) continue;
|
|
18
|
-
const rawValue =
|
|
19
|
+
const rawValue = resolved[field.name];
|
|
19
20
|
const isArray = Array.isArray(rawValue);
|
|
20
21
|
const values = isArray ? rawValue : [rawValue];
|
|
21
22
|
const resolvedIds = [];
|
|
@@ -28,6 +29,12 @@ async function resolveRelations(payload, collectionSlug, data, config) {
|
|
|
28
29
|
});
|
|
29
30
|
if (found.docs.length > 0) {
|
|
30
31
|
resolvedIds.push(found.docs[0].id);
|
|
32
|
+
} else if (mapping.onNotFound === "create") {
|
|
33
|
+
const created = await payload.create({
|
|
34
|
+
collection: relationTo,
|
|
35
|
+
data: { [mapping.lookupField]: val }
|
|
36
|
+
});
|
|
37
|
+
resolvedIds.push(created.id);
|
|
31
38
|
} else if (mapping.onNotFound === "error") {
|
|
32
39
|
throw new Error(`Relation not found: ${relationTo} (${mapping.lookupField}=${val})`);
|
|
33
40
|
} else {
|
|
@@ -101,7 +108,6 @@ async function execute(payload, collection, action, input, config) {
|
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
export {
|
|
104
|
-
__require,
|
|
105
111
|
resolveRelations,
|
|
106
112
|
execute
|
|
107
113
|
};
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
execute: () => execute,
|
|
34
|
+
resolveRelations: () => resolveRelations
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(index_exports);
|
|
37
|
+
|
|
38
|
+
// src/resolver.ts
|
|
39
|
+
async function resolveRelations(payload, collectionSlug, data, config) {
|
|
40
|
+
const collection = payload.collections[collectionSlug];
|
|
41
|
+
if (!collection) throw new Error(`Collection "${collectionSlug}" not found.`);
|
|
42
|
+
const resolved = { ...data };
|
|
43
|
+
const mappingConfig = config.mappings[collectionSlug];
|
|
44
|
+
if (mappingConfig?.defaults) {
|
|
45
|
+
for (const [k, v] of Object.entries(mappingConfig.defaults)) {
|
|
46
|
+
if (resolved[k] === void 0 || resolved[k] === null) {
|
|
47
|
+
resolved[k] = v;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const field of collection.config.fields) {
|
|
52
|
+
if (field.type === "relationship" && resolved[field.name]) {
|
|
53
|
+
const relationTo = Array.isArray(field.relationTo) ? field.relationTo[0] : field.relationTo;
|
|
54
|
+
const mapping = config.mappings[relationTo];
|
|
55
|
+
if (!mapping) continue;
|
|
56
|
+
const rawValue = resolved[field.name];
|
|
57
|
+
const isArray = Array.isArray(rawValue);
|
|
58
|
+
const values = isArray ? rawValue : [rawValue];
|
|
59
|
+
const resolvedIds = [];
|
|
60
|
+
for (const val of values) {
|
|
61
|
+
if (typeof val === "string" && val.length < 24) {
|
|
62
|
+
const found = await payload.find({
|
|
63
|
+
collection: relationTo,
|
|
64
|
+
where: { [mapping.lookupField]: { equals: val } },
|
|
65
|
+
limit: 1
|
|
66
|
+
});
|
|
67
|
+
if (found.docs.length > 0) {
|
|
68
|
+
resolvedIds.push(found.docs[0].id);
|
|
69
|
+
} else if (mapping.onNotFound === "create") {
|
|
70
|
+
const created = await payload.create({
|
|
71
|
+
collection: relationTo,
|
|
72
|
+
data: { [mapping.lookupField]: val }
|
|
73
|
+
});
|
|
74
|
+
resolvedIds.push(created.id);
|
|
75
|
+
} else if (mapping.onNotFound === "error") {
|
|
76
|
+
throw new Error(`Relation not found: ${relationTo} (${mapping.lookupField}=${val})`);
|
|
77
|
+
} else {
|
|
78
|
+
resolvedIds.push(val);
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
resolvedIds.push(val);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
resolved[field.name] = isArray ? resolvedIds : resolvedIds[0];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return resolved;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/executor.ts
|
|
91
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
92
|
+
var import_readline = __toESM(require("readline"), 1);
|
|
93
|
+
var import_path = __toESM(require("path"), 1);
|
|
94
|
+
async function processSingle(payload, collection, action, data, config) {
|
|
95
|
+
const mapping = config.mappings[collection];
|
|
96
|
+
const lookupField = mapping?.lookupField || "id";
|
|
97
|
+
const resolved = await resolveRelations(payload, collection, data, config);
|
|
98
|
+
switch (action) {
|
|
99
|
+
case "create":
|
|
100
|
+
return await payload.create({ collection, data: resolved });
|
|
101
|
+
case "upsert":
|
|
102
|
+
if (data[lookupField] === void 0) {
|
|
103
|
+
throw new Error(`[upsert] Missing lookup field '${lookupField}' in data. Cannot perform upsert.`);
|
|
104
|
+
}
|
|
105
|
+
const existingUpsert = await payload.find({
|
|
106
|
+
collection,
|
|
107
|
+
where: { [lookupField]: { equals: data[lookupField] } }
|
|
108
|
+
});
|
|
109
|
+
if (existingUpsert.docs.length > 0) {
|
|
110
|
+
return await payload.update({ collection, id: existingUpsert.docs[0].id, data: resolved });
|
|
111
|
+
}
|
|
112
|
+
return await payload.create({ collection, data: resolved });
|
|
113
|
+
case "update":
|
|
114
|
+
if (data[lookupField] === void 0) {
|
|
115
|
+
throw new Error(`[update] Missing lookup field '${lookupField}' in data. Cannot perform update.`);
|
|
116
|
+
}
|
|
117
|
+
return await payload.update({
|
|
118
|
+
collection,
|
|
119
|
+
where: { [lookupField]: { equals: data[lookupField] } },
|
|
120
|
+
data: resolved
|
|
121
|
+
});
|
|
122
|
+
case "delete":
|
|
123
|
+
const delVal = typeof data === "object" ? data[lookupField] : data;
|
|
124
|
+
if (delVal === void 0) {
|
|
125
|
+
throw new Error(`[delete] Missing lookup field '${lookupField}' in action. Cannot perform delete.`);
|
|
126
|
+
}
|
|
127
|
+
return await payload.delete({
|
|
128
|
+
collection,
|
|
129
|
+
where: { [lookupField]: { equals: delVal } }
|
|
130
|
+
});
|
|
131
|
+
default:
|
|
132
|
+
throw new Error(`Unsupported action: ${action}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function execute(payload, collection, action, input, config) {
|
|
136
|
+
if (input.endsWith(".jsonl")) {
|
|
137
|
+
const filePath = import_path.default.resolve(process.cwd(), input);
|
|
138
|
+
const rl = import_readline.default.createInterface({ input: import_fs.default.createReadStream(filePath) });
|
|
139
|
+
for await (const line of rl) {
|
|
140
|
+
if (line.trim()) await processSingle(payload, collection, action, JSON.parse(line), config);
|
|
141
|
+
}
|
|
142
|
+
return { status: "bulk success" };
|
|
143
|
+
}
|
|
144
|
+
return await processSingle(payload, collection, action, JSON.parse(input), config);
|
|
145
|
+
}
|
|
146
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
147
|
+
0 && (module.exports = {
|
|
148
|
+
execute,
|
|
149
|
+
resolveRelations
|
|
150
|
+
});
|
|
@@ -3,10 +3,11 @@ import { Payload } from 'payload';
|
|
|
3
3
|
|
|
4
4
|
interface MappingConfig {
|
|
5
5
|
lookupField: string;
|
|
6
|
-
onNotFound
|
|
6
|
+
onNotFound: 'error' | 'ignore' | 'create';
|
|
7
|
+
defaults?: Record<string, any>;
|
|
7
8
|
}
|
|
8
9
|
interface CLIConfig {
|
|
9
|
-
mappings: Record<string, MappingConfig>;
|
|
10
|
+
mappings: Record<string, MappingConfig | undefined>;
|
|
10
11
|
}
|
|
11
12
|
type Action = 'create' | 'update' | 'delete' | 'find' | 'upsert';
|
|
12
13
|
interface CommandArgs {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,10 +3,11 @@ import { Payload } from 'payload';
|
|
|
3
3
|
|
|
4
4
|
interface MappingConfig {
|
|
5
5
|
lookupField: string;
|
|
6
|
-
onNotFound
|
|
6
|
+
onNotFound: 'error' | 'ignore' | 'create';
|
|
7
|
+
defaults?: Record<string, any>;
|
|
7
8
|
}
|
|
8
9
|
interface CLIConfig {
|
|
9
|
-
mappings: Record<string, MappingConfig>;
|
|
10
|
+
mappings: Record<string, MappingConfig | undefined>;
|
|
10
11
|
}
|
|
11
12
|
type Action = 'create' | 'update' | 'delete' | 'find' | 'upsert';
|
|
12
13
|
interface CommandArgs {
|
package/dist/index.js
CHANGED
|
@@ -1,136 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/index.ts
|
|
31
|
-
var index_exports = {};
|
|
32
|
-
__export(index_exports, {
|
|
33
|
-
execute: () => execute,
|
|
34
|
-
resolveRelations: () => resolveRelations
|
|
35
|
-
});
|
|
36
|
-
module.exports = __toCommonJS(index_exports);
|
|
37
|
-
|
|
38
|
-
// src/resolver.ts
|
|
39
|
-
async function resolveRelations(payload, collectionSlug, data, config) {
|
|
40
|
-
const collection = payload.collections[collectionSlug];
|
|
41
|
-
if (!collection) throw new Error(`Collection "${collectionSlug}" not found.`);
|
|
42
|
-
const resolved = { ...data };
|
|
43
|
-
for (const field of collection.config.fields) {
|
|
44
|
-
if (field.type === "relationship" && data[field.name]) {
|
|
45
|
-
const relationTo = Array.isArray(field.relationTo) ? field.relationTo[0] : field.relationTo;
|
|
46
|
-
const mapping = config.mappings[relationTo];
|
|
47
|
-
if (!mapping) continue;
|
|
48
|
-
const rawValue = data[field.name];
|
|
49
|
-
const isArray = Array.isArray(rawValue);
|
|
50
|
-
const values = isArray ? rawValue : [rawValue];
|
|
51
|
-
const resolvedIds = [];
|
|
52
|
-
for (const val of values) {
|
|
53
|
-
if (typeof val === "string" && val.length < 24) {
|
|
54
|
-
const found = await payload.find({
|
|
55
|
-
collection: relationTo,
|
|
56
|
-
where: { [mapping.lookupField]: { equals: val } },
|
|
57
|
-
limit: 1
|
|
58
|
-
});
|
|
59
|
-
if (found.docs.length > 0) {
|
|
60
|
-
resolvedIds.push(found.docs[0].id);
|
|
61
|
-
} else if (mapping.onNotFound === "error") {
|
|
62
|
-
throw new Error(`Relation not found: ${relationTo} (${mapping.lookupField}=${val})`);
|
|
63
|
-
} else {
|
|
64
|
-
resolvedIds.push(val);
|
|
65
|
-
}
|
|
66
|
-
} else {
|
|
67
|
-
resolvedIds.push(val);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
resolved[field.name] = isArray ? resolvedIds : resolvedIds[0];
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return resolved;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// src/executor.ts
|
|
77
|
-
var import_fs = __toESM(require("fs"));
|
|
78
|
-
var import_readline = __toESM(require("readline"));
|
|
79
|
-
var import_path = __toESM(require("path"));
|
|
80
|
-
async function processSingle(payload, collection, action, data, config) {
|
|
81
|
-
const mapping = config.mappings[collection];
|
|
82
|
-
const lookupField = mapping?.lookupField || "id";
|
|
83
|
-
const resolved = await resolveRelations(payload, collection, data, config);
|
|
84
|
-
switch (action) {
|
|
85
|
-
case "create":
|
|
86
|
-
return await payload.create({ collection, data: resolved });
|
|
87
|
-
case "upsert":
|
|
88
|
-
if (data[lookupField] === void 0) {
|
|
89
|
-
throw new Error(`[upsert] Missing lookup field '${lookupField}' in data. Cannot perform upsert.`);
|
|
90
|
-
}
|
|
91
|
-
const existingUpsert = await payload.find({
|
|
92
|
-
collection,
|
|
93
|
-
where: { [lookupField]: { equals: data[lookupField] } }
|
|
94
|
-
});
|
|
95
|
-
if (existingUpsert.docs.length > 0) {
|
|
96
|
-
return await payload.update({ collection, id: existingUpsert.docs[0].id, data: resolved });
|
|
97
|
-
}
|
|
98
|
-
return await payload.create({ collection, data: resolved });
|
|
99
|
-
case "update":
|
|
100
|
-
if (data[lookupField] === void 0) {
|
|
101
|
-
throw new Error(`[update] Missing lookup field '${lookupField}' in data. Cannot perform update.`);
|
|
102
|
-
}
|
|
103
|
-
return await payload.update({
|
|
104
|
-
collection,
|
|
105
|
-
where: { [lookupField]: { equals: data[lookupField] } },
|
|
106
|
-
data: resolved
|
|
107
|
-
});
|
|
108
|
-
case "delete":
|
|
109
|
-
const delVal = typeof data === "object" ? data[lookupField] : data;
|
|
110
|
-
if (delVal === void 0) {
|
|
111
|
-
throw new Error(`[delete] Missing lookup field '${lookupField}' in action. Cannot perform delete.`);
|
|
112
|
-
}
|
|
113
|
-
return await payload.delete({
|
|
114
|
-
collection,
|
|
115
|
-
where: { [lookupField]: { equals: delVal } }
|
|
116
|
-
});
|
|
117
|
-
default:
|
|
118
|
-
throw new Error(`Unsupported action: ${action}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
async function execute(payload, collection, action, input, config) {
|
|
122
|
-
if (input.endsWith(".jsonl")) {
|
|
123
|
-
const filePath = import_path.default.resolve(process.cwd(), input);
|
|
124
|
-
const rl = import_readline.default.createInterface({ input: import_fs.default.createReadStream(filePath) });
|
|
125
|
-
for await (const line of rl) {
|
|
126
|
-
if (line.trim()) await processSingle(payload, collection, action, JSON.parse(line), config);
|
|
127
|
-
}
|
|
128
|
-
return { status: "bulk success" };
|
|
129
|
-
}
|
|
130
|
-
return await processSingle(payload, collection, action, JSON.parse(input), config);
|
|
131
|
-
}
|
|
132
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
133
|
-
0 && (module.exports = {
|
|
1
|
+
import {
|
|
134
2
|
execute,
|
|
135
3
|
resolveRelations
|
|
136
|
-
}
|
|
4
|
+
} from "./chunk-D5RS7XAO.js";
|
|
5
|
+
export {
|
|
6
|
+
execute,
|
|
7
|
+
resolveRelations
|
|
8
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@payload-cc/payload-collection-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "Functional CLI for Payload 3.0 collection management",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
@@ -23,7 +24,10 @@
|
|
|
23
24
|
"build": "tsup src/index.ts src/bin.ts --format cjs,esm --dts --clean --shims && chmod +x dist/bin.js",
|
|
24
25
|
"release": "pnpm build && pnpm publish --access public",
|
|
25
26
|
"test": "vitest run tests/*",
|
|
26
|
-
"test:e2e": "vitest run e2e-tests/scenarios/ --no-file-parallelism"
|
|
27
|
+
"test:e2e": "vitest run e2e-tests/scenarios/ --no-file-parallelism",
|
|
28
|
+
"docs:dev": "vitepress dev docs",
|
|
29
|
+
"docs:build": "vitepress build docs",
|
|
30
|
+
"docs:preview": "vitepress preview docs"
|
|
27
31
|
},
|
|
28
32
|
"keywords": [
|
|
29
33
|
"payload",
|
|
@@ -37,13 +41,17 @@
|
|
|
37
41
|
"payload": "^3.0.0"
|
|
38
42
|
},
|
|
39
43
|
"dependencies": {
|
|
40
|
-
"jiti": "^2.6.1"
|
|
44
|
+
"jiti": "^2.6.1",
|
|
45
|
+
"yargs": "^18.0.0",
|
|
46
|
+
"zod": "^4.3.6"
|
|
41
47
|
},
|
|
42
48
|
"devDependencies": {
|
|
43
49
|
"@types/node": "^25.5.0",
|
|
50
|
+
"@types/yargs": "^17.0.35",
|
|
44
51
|
"payload": "^3.79.1",
|
|
45
52
|
"tsup": "^8.5.1",
|
|
46
53
|
"typescript": "^5.9.3",
|
|
54
|
+
"vitepress": "^1.6.4",
|
|
47
55
|
"vitest": "^4.1.0"
|
|
48
56
|
}
|
|
49
57
|
}
|
package/dist/bin.mjs
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
__require,
|
|
4
|
-
execute
|
|
5
|
-
} from "./chunk-I7HHN7EL.mjs";
|
|
6
|
-
|
|
7
|
-
// src/bin.ts
|
|
8
|
-
import { getPayload } from "payload";
|
|
9
|
-
import { createJiti } from "jiti";
|
|
10
|
-
import path from "path";
|
|
11
|
-
var jiti = createJiti(import.meta.url);
|
|
12
|
-
async function run() {
|
|
13
|
-
const args = process.argv.slice(2);
|
|
14
|
-
let configOptIdx = args.indexOf("-c");
|
|
15
|
-
if (configOptIdx === -1) configOptIdx = args.indexOf("--config");
|
|
16
|
-
let cliConfig = { mappings: {} };
|
|
17
|
-
if (configOptIdx !== -1) {
|
|
18
|
-
if (args.length <= configOptIdx + 1) {
|
|
19
|
-
console.error("\u274C Error: Missing option after --config parameter.");
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
const val = args[configOptIdx + 1];
|
|
23
|
-
args.splice(configOptIdx, 2);
|
|
24
|
-
if (val.trim().startsWith("{")) {
|
|
25
|
-
try {
|
|
26
|
-
cliConfig = JSON.parse(val);
|
|
27
|
-
} catch (err) {
|
|
28
|
-
console.error("\u274C Error: Failed to parse inline JSON config:", err);
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
} else {
|
|
32
|
-
const customConfigPath = path.resolve(process.cwd(), val);
|
|
33
|
-
if (!__require("fs").existsSync(customConfigPath)) {
|
|
34
|
-
console.error(`\u274C Error: Config file not found at ${customConfigPath}`);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
const imported = await jiti.import(customConfigPath);
|
|
38
|
-
cliConfig = imported.cliConfig || imported.default || cliConfig;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
const [collection, action, input] = args;
|
|
42
|
-
const root = process.cwd();
|
|
43
|
-
if (!collection || !action || !input) {
|
|
44
|
-
console.log("Usage: payload-collection-cli [-c path] <collection> <action> <json|file.jsonl>");
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
const configPath = [
|
|
48
|
-
path.resolve(root, "src/payload.config.ts"),
|
|
49
|
-
path.resolve(root, "payload.config.ts")
|
|
50
|
-
].find((p) => __require("fs").existsSync(p));
|
|
51
|
-
if (!configPath) throw new Error("payload.config.ts not found.");
|
|
52
|
-
const { default: payloadConfig } = await jiti.import(configPath);
|
|
53
|
-
const payload = await getPayload({ config: payloadConfig });
|
|
54
|
-
try {
|
|
55
|
-
const result = await execute(payload, collection, action, input, cliConfig);
|
|
56
|
-
console.log("\u2728 Operation successful");
|
|
57
|
-
} catch (err) {
|
|
58
|
-
console.error("\u274C Error:", err.message);
|
|
59
|
-
process.exit(1);
|
|
60
|
-
}
|
|
61
|
-
process.exit(0);
|
|
62
|
-
}
|
|
63
|
-
run();
|
package/dist/index.mjs
DELETED
|
File without changes
|