@payload-cc/payload-collection-cli 1.2.0 → 1.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/README.md +31 -29
- package/dist/bin.cjs +239 -0
- package/dist/bin.js +78 -176
- package/dist/{chunk-RIXWYITK.mjs → chunk-IU4EF26D.js} +1 -8
- package/dist/index.cjs +151 -0
- package/dist/{index.d.mts → index.d.cts} +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -148
- package/package.json +12 -4
- 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,35 @@
|
|
|
1
1
|
# payload-collection-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/payload-cc/payload-collection-cli/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@payload-cc/payload-collection-cli)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://payload-cc.github.io/payload-collection-cli/)
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
**The missing CLI for Payload 3.0. Manage collections without writing boilerplate scripts.**
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
<img src="docs/public/logo.png" alt="Payload Collection CLI Logo" width="200"/>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
🚀 <strong><a href="https://payload-cc.github.io/payload-collection-cli/">Official Guide</a></strong>
|
|
16
|
+
•
|
|
17
|
+
🤖 <strong><a href="https://payload-cc.github.io/payload-collection-cli/ai.html">AI Context Page</a></strong>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Why?
|
|
23
|
+
|
|
24
|
+
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.
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- ⚡️ **Zero Scripting:** Run CRUD operations directly from your terminal.
|
|
29
|
+
- 🔍 **Relation Lookup:** Resolve relationships using `name`, `email`, or `slug`.
|
|
30
|
+
- 📄 **Batch Processing:** Support for both JSON strings and JSONLines (`.jsonl`) files.
|
|
31
|
+
- ⚙️ **Configurable:** Define your lookup logic in a simple external config.
|
|
32
|
+
- 🛡 **Native Performance:** Uses Local API to ensure all Hooks and Validations run.
|
|
6
33
|
|
|
7
34
|
## Installation
|
|
8
35
|
|
|
@@ -17,7 +44,7 @@ You can immediately start using the commands without any configuration for basic
|
|
|
17
44
|
|
|
18
45
|
**Command Syntax**:
|
|
19
46
|
```bash
|
|
20
|
-
npx @payload-cc/payload-collection-cli [
|
|
47
|
+
npx @payload-cc/payload-collection-cli [options] <collection-slug> <operation> <file or string>
|
|
21
48
|
```
|
|
22
49
|
|
|
23
50
|
**Examples**:
|
|
@@ -39,33 +66,8 @@ npx @payload-cc/payload-collection-cli -c ./my-map.config.ts users upsert data.j
|
|
|
39
66
|
- `upsert`: Update if existing, create if not.
|
|
40
67
|
|
|
41
68
|
## 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
69
|
|
|
68
|
-
|
|
70
|
+
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
71
|
|
|
70
72
|
## Development & CI/CD
|
|
71
73
|
|
package/dist/bin.cjs
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
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
|
+
console.log(`\u{1F4DD} [executor] Creating record in "${collection}" with resolved data:`, JSON.stringify(resolved, null, 2));
|
|
104
|
+
return await payload.create({ collection, data: resolved });
|
|
105
|
+
case "upsert":
|
|
106
|
+
if (data[lookupField] === void 0) {
|
|
107
|
+
throw new Error(`[upsert] Missing lookup field '${lookupField}' in data. Cannot perform upsert.`);
|
|
108
|
+
}
|
|
109
|
+
const existingUpsert = await payload.find({
|
|
110
|
+
collection,
|
|
111
|
+
where: { [lookupField]: { equals: data[lookupField] } }
|
|
112
|
+
});
|
|
113
|
+
if (existingUpsert.docs.length > 0) {
|
|
114
|
+
return await payload.update({ collection, id: existingUpsert.docs[0].id, data: resolved });
|
|
115
|
+
}
|
|
116
|
+
return await payload.create({ collection, data: resolved });
|
|
117
|
+
case "update":
|
|
118
|
+
if (data[lookupField] === void 0) {
|
|
119
|
+
throw new Error(`[update] Missing lookup field '${lookupField}' in data. Cannot perform update.`);
|
|
120
|
+
}
|
|
121
|
+
return await payload.update({
|
|
122
|
+
collection,
|
|
123
|
+
where: { [lookupField]: { equals: data[lookupField] } },
|
|
124
|
+
data: resolved
|
|
125
|
+
});
|
|
126
|
+
case "delete":
|
|
127
|
+
const delVal = typeof data === "object" ? data[lookupField] : data;
|
|
128
|
+
if (delVal === void 0) {
|
|
129
|
+
throw new Error(`[delete] Missing lookup field '${lookupField}' in action. Cannot perform delete.`);
|
|
130
|
+
}
|
|
131
|
+
return await payload.delete({
|
|
132
|
+
collection,
|
|
133
|
+
where: { [lookupField]: { equals: delVal } }
|
|
134
|
+
});
|
|
135
|
+
default:
|
|
136
|
+
throw new Error(`Unsupported action: ${action}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function execute(payload, collection, action, input, config) {
|
|
140
|
+
if (input.endsWith(".jsonl")) {
|
|
141
|
+
const filePath = import_path.default.resolve(process.cwd(), input);
|
|
142
|
+
const rl = import_readline.default.createInterface({ input: import_fs.default.createReadStream(filePath) });
|
|
143
|
+
for await (const line of rl) {
|
|
144
|
+
if (line.trim()) await processSingle(payload, collection, action, JSON.parse(line), config);
|
|
145
|
+
}
|
|
146
|
+
return { status: "bulk success" };
|
|
147
|
+
}
|
|
148
|
+
return await processSingle(payload, collection, action, JSON.parse(input), config);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/bin.ts
|
|
152
|
+
var jiti = (0, import_jiti.createJiti)(importMetaUrl);
|
|
153
|
+
var MappingConfigSchema = import_zod.z.object({
|
|
154
|
+
lookupField: import_zod.z.string().default("id"),
|
|
155
|
+
onNotFound: import_zod.z.enum(["error", "ignore", "create"]).default("error"),
|
|
156
|
+
defaults: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional()
|
|
157
|
+
});
|
|
158
|
+
var CLIConfigSchema = import_zod.z.object({
|
|
159
|
+
mappings: import_zod.z.record(import_zod.z.string(), MappingConfigSchema).default({})
|
|
160
|
+
});
|
|
161
|
+
async function run() {
|
|
162
|
+
console.log("\u{1F3C1} Starting CLI...");
|
|
163
|
+
const root = process.cwd();
|
|
164
|
+
const argv = await (0, import_yargs.default)((0, import_helpers.hideBin)(process.argv)).usage("Usage: $0 [options] <collection-slug> <operation> <file or string>").options({
|
|
165
|
+
"config-file": {
|
|
166
|
+
alias: "c",
|
|
167
|
+
type: "string",
|
|
168
|
+
describe: "Path to a configuration file (named export required)."
|
|
169
|
+
},
|
|
170
|
+
"config-json": {
|
|
171
|
+
alias: "j",
|
|
172
|
+
type: "string",
|
|
173
|
+
describe: "Inline JSON string for configuration."
|
|
174
|
+
},
|
|
175
|
+
"config-export-name": {
|
|
176
|
+
alias: "n",
|
|
177
|
+
type: "string",
|
|
178
|
+
describe: "The name of the export to use from the configuration file.",
|
|
179
|
+
default: "cliConfig"
|
|
180
|
+
}
|
|
181
|
+
}).pkgConf("payload-collection-cli").demandCommand(3, "Collection slug, operation, and input are required.").help().parseSync();
|
|
182
|
+
console.log("\u{1F4CA} Parsed arguments:", JSON.stringify(argv, null, 2));
|
|
183
|
+
const { configFile, configJson, configExportName, _: [collection, action, input] } = argv;
|
|
184
|
+
let rawConfig = { mappings: {} };
|
|
185
|
+
if (configJson) {
|
|
186
|
+
try {
|
|
187
|
+
rawConfig = typeof configJson === "string" ? JSON.parse(configJson) : configJson;
|
|
188
|
+
} catch (err) {
|
|
189
|
+
console.error("\u274C Error: Failed to parse inline JSON config:", err);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
} else if (configFile) {
|
|
193
|
+
const customConfigPath = import_path2.default.resolve(root, configFile);
|
|
194
|
+
if (!import_fs2.default.existsSync(customConfigPath)) {
|
|
195
|
+
console.error(`\u274C Error: Config file not found at ${customConfigPath}`);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
}
|
|
198
|
+
const imported = await jiti.import(customConfigPath);
|
|
199
|
+
if (imported[configExportName]) {
|
|
200
|
+
rawConfig = imported[configExportName];
|
|
201
|
+
} else {
|
|
202
|
+
console.error(`\u274C Error: Named export "${configExportName}" not found in ${configFile}`);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
const validation = CLIConfigSchema.safeParse(rawConfig);
|
|
207
|
+
if (!validation.success) {
|
|
208
|
+
console.error("\u274C Error: Invalid configuration structure:");
|
|
209
|
+
validation.error.issues.forEach((issue) => {
|
|
210
|
+
console.error(` - [${issue.path.join(".")}] ${issue.message}`);
|
|
211
|
+
});
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
const cliConfig = validation.data;
|
|
215
|
+
const configPath = [
|
|
216
|
+
import_path2.default.resolve(root, "src/payload.config.ts"),
|
|
217
|
+
import_path2.default.resolve(root, "payload.config.ts")
|
|
218
|
+
].find((p) => import_fs2.default.existsSync(p));
|
|
219
|
+
if (!configPath) {
|
|
220
|
+
console.error("\u274C Error: payload.config.ts not found.");
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
console.log(`\u{1F4D6} Loading payload config from: ${configPath}`);
|
|
224
|
+
const { default: payloadConfig } = await jiti.import(configPath);
|
|
225
|
+
const payload = await (0, import_payload.getPayload)({ config: payloadConfig });
|
|
226
|
+
console.log("\u2705 Connected to Payload");
|
|
227
|
+
try {
|
|
228
|
+
const result = await execute(payload, collection, action, input, cliConfig);
|
|
229
|
+
console.log("\u2728 Operation successful");
|
|
230
|
+
} catch (err) {
|
|
231
|
+
console.error("\u274C Error:", err.message);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
process.exit(0);
|
|
235
|
+
}
|
|
236
|
+
run().catch((err) => {
|
|
237
|
+
console.error("\u274C Fatal Error:", err.message);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
});
|
package/dist/bin.js
CHANGED
|
@@ -1,192 +1,91 @@
|
|
|
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-IU4EF26D.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
|
-
where: { [mapping.lookupField]: { equals: val } },
|
|
67
|
-
limit: 1
|
|
68
|
-
});
|
|
69
|
-
if (found.docs.length > 0) {
|
|
70
|
-
resolvedIds.push(found.docs[0].id);
|
|
71
|
-
} else if (mapping.onNotFound === "create") {
|
|
72
|
-
const created = await payload.create({
|
|
73
|
-
collection: relationTo,
|
|
74
|
-
data: { [mapping.lookupField]: val }
|
|
75
|
-
});
|
|
76
|
-
resolvedIds.push(created.id);
|
|
77
|
-
} else if (mapping.onNotFound === "error") {
|
|
78
|
-
throw new Error(`Relation not found: ${relationTo} (${mapping.lookupField}=${val})`);
|
|
79
|
-
} else {
|
|
80
|
-
resolvedIds.push(val);
|
|
81
|
-
}
|
|
82
|
-
} else {
|
|
83
|
-
resolvedIds.push(val);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
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);
|
|
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"
|
|
87
42
|
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
case "create":
|
|
99
|
-
return await payload.create({ collection, data: resolved });
|
|
100
|
-
case "upsert":
|
|
101
|
-
if (data[lookupField] === void 0) {
|
|
102
|
-
throw new Error(`[upsert] Missing lookup field '${lookupField}' in data. Cannot perform upsert.`);
|
|
103
|
-
}
|
|
104
|
-
const existingUpsert = await payload.find({
|
|
105
|
-
collection,
|
|
106
|
-
where: { [lookupField]: { equals: data[lookupField] } }
|
|
107
|
-
});
|
|
108
|
-
if (existingUpsert.docs.length > 0) {
|
|
109
|
-
return await payload.update({ collection, id: existingUpsert.docs[0].id, data: resolved });
|
|
110
|
-
}
|
|
111
|
-
return await payload.create({ collection, data: resolved });
|
|
112
|
-
case "update":
|
|
113
|
-
if (data[lookupField] === void 0) {
|
|
114
|
-
throw new Error(`[update] Missing lookup field '${lookupField}' in data. Cannot perform update.`);
|
|
115
|
-
}
|
|
116
|
-
return await payload.update({
|
|
117
|
-
collection,
|
|
118
|
-
where: { [lookupField]: { equals: data[lookupField] } },
|
|
119
|
-
data: resolved
|
|
120
|
-
});
|
|
121
|
-
case "delete":
|
|
122
|
-
const delVal = typeof data === "object" ? data[lookupField] : data;
|
|
123
|
-
if (delVal === void 0) {
|
|
124
|
-
throw new Error(`[delete] Missing lookup field '${lookupField}' in action. Cannot perform delete.`);
|
|
125
|
-
}
|
|
126
|
-
return await payload.delete({
|
|
127
|
-
collection,
|
|
128
|
-
where: { [lookupField]: { equals: delVal } }
|
|
129
|
-
});
|
|
130
|
-
default:
|
|
131
|
-
throw new Error(`Unsupported action: ${action}`);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
async function execute(payload, collection, action, input, config) {
|
|
135
|
-
if (input.endsWith(".jsonl")) {
|
|
136
|
-
const filePath = import_path.default.resolve(process.cwd(), input);
|
|
137
|
-
const rl = import_readline.default.createInterface({ input: import_fs.default.createReadStream(filePath) });
|
|
138
|
-
for await (const line of rl) {
|
|
139
|
-
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().parseSync();
|
|
44
|
+
console.log("\u{1F4CA} Parsed arguments:", JSON.stringify(argv, null, 2));
|
|
45
|
+
const { configFile, configJson, configExportName, _: [collection, action, input] } = argv;
|
|
46
|
+
let rawConfig = { mappings: {} };
|
|
47
|
+
if (configJson) {
|
|
48
|
+
try {
|
|
49
|
+
rawConfig = typeof configJson === "string" ? JSON.parse(configJson) : configJson;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.error("\u274C Error: Failed to parse inline JSON config:", err);
|
|
52
|
+
process.exit(1);
|
|
140
53
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// src/bin.ts
|
|
147
|
-
var jiti = (0, import_jiti.createJiti)(importMetaUrl);
|
|
148
|
-
async function run() {
|
|
149
|
-
const args = process.argv.slice(2);
|
|
150
|
-
let configOptIdx = args.indexOf("-c");
|
|
151
|
-
if (configOptIdx === -1) configOptIdx = args.indexOf("--config");
|
|
152
|
-
let cliConfig = { mappings: {} };
|
|
153
|
-
if (configOptIdx !== -1) {
|
|
154
|
-
if (args.length <= configOptIdx + 1) {
|
|
155
|
-
console.error("\u274C Error: Missing option after --config parameter.");
|
|
54
|
+
} else if (configFile) {
|
|
55
|
+
const customConfigPath = path.resolve(root, configFile);
|
|
56
|
+
if (!fs.existsSync(customConfigPath)) {
|
|
57
|
+
console.error(`\u274C Error: Config file not found at ${customConfigPath}`);
|
|
156
58
|
process.exit(1);
|
|
157
59
|
}
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
cliConfig = JSON.parse(val);
|
|
163
|
-
} catch (err) {
|
|
164
|
-
console.error("\u274C Error: Failed to parse inline JSON config:", err);
|
|
165
|
-
process.exit(1);
|
|
166
|
-
}
|
|
60
|
+
const imported = await jiti.import(customConfigPath);
|
|
61
|
+
if (imported[configExportName]) {
|
|
62
|
+
rawConfig = imported[configExportName];
|
|
167
63
|
} else {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
console.error(`\u274C Error: Config file not found at ${customConfigPath}`);
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
173
|
-
const imported = await jiti.import(customConfigPath);
|
|
174
|
-
cliConfig = imported.cliConfig || imported.default || cliConfig;
|
|
64
|
+
console.error(`\u274C Error: Named export "${configExportName}" not found in ${configFile}`);
|
|
65
|
+
process.exit(1);
|
|
175
66
|
}
|
|
176
67
|
}
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
68
|
+
const validation = CLIConfigSchema.safeParse(rawConfig);
|
|
69
|
+
if (!validation.success) {
|
|
70
|
+
console.error("\u274C Error: Invalid configuration structure:");
|
|
71
|
+
validation.error.issues.forEach((issue) => {
|
|
72
|
+
console.error(` - [${issue.path.join(".")}] ${issue.message}`);
|
|
73
|
+
});
|
|
181
74
|
process.exit(1);
|
|
182
75
|
}
|
|
76
|
+
const cliConfig = validation.data;
|
|
183
77
|
const configPath = [
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
].find((p) =>
|
|
187
|
-
if (!configPath)
|
|
78
|
+
path.resolve(root, "src/payload.config.ts"),
|
|
79
|
+
path.resolve(root, "payload.config.ts")
|
|
80
|
+
].find((p) => fs.existsSync(p));
|
|
81
|
+
if (!configPath) {
|
|
82
|
+
console.error("\u274C Error: payload.config.ts not found.");
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
console.log(`\u{1F4D6} Loading payload config from: ${configPath}`);
|
|
188
86
|
const { default: payloadConfig } = await jiti.import(configPath);
|
|
189
|
-
const payload = await
|
|
87
|
+
const payload = await getPayload({ config: payloadConfig });
|
|
88
|
+
console.log("\u2705 Connected to Payload");
|
|
190
89
|
try {
|
|
191
90
|
const result = await execute(payload, collection, action, input, cliConfig);
|
|
192
91
|
console.log("\u2728 Operation successful");
|
|
@@ -196,4 +95,7 @@ async function run() {
|
|
|
196
95
|
}
|
|
197
96
|
process.exit(0);
|
|
198
97
|
}
|
|
199
|
-
run()
|
|
98
|
+
run().catch((err) => {
|
|
99
|
+
console.error("\u274C Fatal Error:", err.message);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
});
|
|
@@ -1,10 +1,3 @@
|
|
|
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];
|
|
@@ -67,6 +60,7 @@ async function processSingle(payload, collection, action, data, config) {
|
|
|
67
60
|
const resolved = await resolveRelations(payload, collection, data, config);
|
|
68
61
|
switch (action) {
|
|
69
62
|
case "create":
|
|
63
|
+
console.log(`\u{1F4DD} [executor] Creating record in "${collection}" with resolved data:`, JSON.stringify(resolved, null, 2));
|
|
70
64
|
return await payload.create({ collection, data: resolved });
|
|
71
65
|
case "upsert":
|
|
72
66
|
if (data[lookupField] === void 0) {
|
|
@@ -115,7 +109,6 @@ async function execute(payload, collection, action, input, config) {
|
|
|
115
109
|
}
|
|
116
110
|
|
|
117
111
|
export {
|
|
118
|
-
__require,
|
|
119
112
|
resolveRelations,
|
|
120
113
|
execute
|
|
121
114
|
};
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
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
|
+
console.log(`\u{1F4DD} [executor] Creating record in "${collection}" with resolved data:`, JSON.stringify(resolved, null, 2));
|
|
101
|
+
return await payload.create({ collection, data: resolved });
|
|
102
|
+
case "upsert":
|
|
103
|
+
if (data[lookupField] === void 0) {
|
|
104
|
+
throw new Error(`[upsert] Missing lookup field '${lookupField}' in data. Cannot perform upsert.`);
|
|
105
|
+
}
|
|
106
|
+
const existingUpsert = await payload.find({
|
|
107
|
+
collection,
|
|
108
|
+
where: { [lookupField]: { equals: data[lookupField] } }
|
|
109
|
+
});
|
|
110
|
+
if (existingUpsert.docs.length > 0) {
|
|
111
|
+
return await payload.update({ collection, id: existingUpsert.docs[0].id, data: resolved });
|
|
112
|
+
}
|
|
113
|
+
return await payload.create({ collection, data: resolved });
|
|
114
|
+
case "update":
|
|
115
|
+
if (data[lookupField] === void 0) {
|
|
116
|
+
throw new Error(`[update] Missing lookup field '${lookupField}' in data. Cannot perform update.`);
|
|
117
|
+
}
|
|
118
|
+
return await payload.update({
|
|
119
|
+
collection,
|
|
120
|
+
where: { [lookupField]: { equals: data[lookupField] } },
|
|
121
|
+
data: resolved
|
|
122
|
+
});
|
|
123
|
+
case "delete":
|
|
124
|
+
const delVal = typeof data === "object" ? data[lookupField] : data;
|
|
125
|
+
if (delVal === void 0) {
|
|
126
|
+
throw new Error(`[delete] Missing lookup field '${lookupField}' in action. Cannot perform delete.`);
|
|
127
|
+
}
|
|
128
|
+
return await payload.delete({
|
|
129
|
+
collection,
|
|
130
|
+
where: { [lookupField]: { equals: delVal } }
|
|
131
|
+
});
|
|
132
|
+
default:
|
|
133
|
+
throw new Error(`Unsupported action: ${action}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function execute(payload, collection, action, input, config) {
|
|
137
|
+
if (input.endsWith(".jsonl")) {
|
|
138
|
+
const filePath = import_path.default.resolve(process.cwd(), input);
|
|
139
|
+
const rl = import_readline.default.createInterface({ input: import_fs.default.createReadStream(filePath) });
|
|
140
|
+
for await (const line of rl) {
|
|
141
|
+
if (line.trim()) await processSingle(payload, collection, action, JSON.parse(line), config);
|
|
142
|
+
}
|
|
143
|
+
return { status: "bulk success" };
|
|
144
|
+
}
|
|
145
|
+
return await processSingle(payload, collection, action, JSON.parse(input), config);
|
|
146
|
+
}
|
|
147
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
148
|
+
0 && (module.exports = {
|
|
149
|
+
execute,
|
|
150
|
+
resolveRelations
|
|
151
|
+
});
|
|
@@ -3,11 +3,11 @@ import { Payload } from 'payload';
|
|
|
3
3
|
|
|
4
4
|
interface MappingConfig {
|
|
5
5
|
lookupField: string;
|
|
6
|
-
onNotFound
|
|
6
|
+
onNotFound: 'error' | 'ignore' | 'create';
|
|
7
7
|
defaults?: Record<string, any>;
|
|
8
8
|
}
|
|
9
9
|
interface CLIConfig {
|
|
10
|
-
mappings: Record<string, MappingConfig>;
|
|
10
|
+
mappings: Record<string, MappingConfig | undefined>;
|
|
11
11
|
}
|
|
12
12
|
type Action = 'create' | 'update' | 'delete' | 'find' | 'upsert';
|
|
13
13
|
interface CommandArgs {
|
package/dist/index.d.ts
CHANGED
|
@@ -3,11 +3,11 @@ import { Payload } from 'payload';
|
|
|
3
3
|
|
|
4
4
|
interface MappingConfig {
|
|
5
5
|
lookupField: string;
|
|
6
|
-
onNotFound
|
|
6
|
+
onNotFound: 'error' | 'ignore' | 'create';
|
|
7
7
|
defaults?: Record<string, any>;
|
|
8
8
|
}
|
|
9
9
|
interface CLIConfig {
|
|
10
|
-
mappings: Record<string, MappingConfig>;
|
|
10
|
+
mappings: Record<string, MappingConfig | undefined>;
|
|
11
11
|
}
|
|
12
12
|
type Action = 'create' | 'update' | 'delete' | 'find' | 'upsert';
|
|
13
13
|
interface CommandArgs {
|
package/dist/index.js
CHANGED
|
@@ -1,150 +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
|
-
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"));
|
|
92
|
-
var import_readline = __toESM(require("readline"));
|
|
93
|
-
var import_path = __toESM(require("path"));
|
|
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 = {
|
|
1
|
+
import {
|
|
148
2
|
execute,
|
|
149
3
|
resolveRelations
|
|
150
|
-
}
|
|
4
|
+
} from "./chunk-IU4EF26D.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.1",
|
|
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 --test-timeout 300000",
|
|
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",
|
|
44
|
-
"
|
|
50
|
+
"@types/yargs": "^17.0.35",
|
|
51
|
+
"payload": "^3.80.0",
|
|
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-RIXWYITK.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
|