@cerios/salesforce-sobjectly 0.0.0-stage → 1.0.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/LICENSE +8 -0
- package/README.md +231 -2
- package/dist/cli.cjs +188 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +196 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/codegen-BgDgU2RR.cjs +1209 -0
- package/dist/codegen-BgDgU2RR.cjs.map +1 -0
- package/dist/codegen-CSx0WAtK.mjs +1110 -0
- package/dist/codegen-CSx0WAtK.mjs.map +1 -0
- package/dist/codegen.cjs +23 -0
- package/dist/codegen.d.cts +312 -0
- package/dist/codegen.d.mts +312 -0
- package/dist/codegen.mjs +10 -0
- package/dist/describe-Dj3J8o-x.d.cts +284 -0
- package/dist/describe-Dj3J8o-x.d.mts +284 -0
- package/dist/index.cjs +65 -0
- package/dist/index.d.cts +2451 -0
- package/dist/index.d.mts +2451 -0
- package/dist/index.mjs +33 -0
- package/dist/index.mjs.map +1 -0
- package/dist/sf-cli-CtWtdkJ0.mjs +3986 -0
- package/dist/sf-cli-CtWtdkJ0.mjs.map +1 -0
- package/dist/sf-cli-DEVZQTR7.cjs +4344 -0
- package/dist/sf-cli-DEVZQTR7.cjs.map +1 -0
- package/package.json +112 -5
- package/sobjectly.config.schema.json +147 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
Copyright © 2026 Cerios
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
5
|
+
|
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,232 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @cerios/salesforce-sobjectly
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A type-safe Salesforce REST API client for TypeScript. It generates types for your org's sObjects and gives you a typed SOQL builder, CRUD, composite requests, sObject collections, SOSL, invocable actions and Flows, the Tooling API and Bulk API 2.0. Requests go through native `fetch` by default, and any other HTTP client can be plugged in.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@cerios/salesforce-sobjectly)
|
|
6
|
+
[](https://www.npmjs.com/package/@cerios/salesforce-sobjectly)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
|
|
10
|
+
## ✨ Key Features
|
|
11
|
+
|
|
12
|
+
- **Generated sObject types**: `sobjectly generate` reads describe metadata and writes a read type, a create input and an update input for every sObject. Required fields, restricted picklists and relationships all come from your org.
|
|
13
|
+
- **Typed SOQL builder**: field names are checked and autocompleted. The query result only contains the fields you selected, so `record.Phone` is a compile error unless you selected `Phone`.
|
|
14
|
+
- **Relationship queries**: `selectRelated("Account", "Name")` and `selectChild("Contacts", c => c.select("Email"))` produce typed nested results.
|
|
15
|
+
- **The whole REST API**: sObject CRUD, upsert by external id, query and queryAll with pagination, SOSL, Composite, Composite Graph, sObject Tree, sObject Collections, invocable actions and Flows, the Tooling API, Apex REST and Bulk API 2.0. Anything else is reachable through `request<T>()`.
|
|
16
|
+
- **Platform APIs**: platform events, approvals, quick actions, UI API, files, reports, query plans and Apex debug-log capture.
|
|
17
|
+
- **CI-friendly codegen**: `sobjectly generate --check` fails when the generated file is out of date and ignores formatting-only differences.
|
|
18
|
+
- **Bring your own HTTP client**: `fetch` is the default; any other client (axios, undici, Playwright's `APIRequestContext`, a logging wrapper) fits a one-method interface.
|
|
19
|
+
- **Built-in auth**: Salesforce CLI login (`sfCli`), static access token, client credentials, JWT bearer, refresh token or your own provider. Tokens are cached, and the client re-authenticates once on a 401.
|
|
20
|
+
- **Safe by default**: values are escaped in SOQL, absolute URLs are limited to your instance's origin, tokens are never logged, and retries are opt-in. Mutations are only retried when you ask for it, e.g. on `UNABLE_TO_LOCK_ROW`, where Salesforce rolled the request back.
|
|
21
|
+
- **Small footprint**: dual ESM/CJS builds. The only runtime dependency is `jiti`, which the codegen CLI uses to load TypeScript configs; the client never loads it.
|
|
22
|
+
|
|
23
|
+
## 📦 Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @cerios/salesforce-sobjectly
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
It needs Node.js 20.19 or later, and TypeScript 5.4 or later for the types.
|
|
30
|
+
|
|
31
|
+
## 🎯 Quick Start
|
|
32
|
+
|
|
33
|
+
### 1. Create a config
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx sobjectly init
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`init` asks a few questions and writes `sobjectly.config.ts` or `sobjectly.config.json`:
|
|
40
|
+
|
|
41
|
+
- the API version, e.g. `v66.0`;
|
|
42
|
+
- where to write the generated types;
|
|
43
|
+
- which sObjects to generate;
|
|
44
|
+
- how to log in;
|
|
45
|
+
- how to type picklists;
|
|
46
|
+
- the config format: TypeScript (type-checked with `defineConfig`) or JSON (with a bundled JSON Schema for editor autocomplete).
|
|
47
|
+
|
|
48
|
+
It can also add the credential variables to `.env.example` and an npm script to `package.json`. Both formats are validated when loaded, and all problems are reported at once. A TypeScript config looks like this:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { defineConfig } from "@cerios/salesforce-sobjectly/codegen";
|
|
52
|
+
|
|
53
|
+
export default defineConfig({
|
|
54
|
+
apiVersion: "v66.0",
|
|
55
|
+
output: "src/generated/sobjects.ts",
|
|
56
|
+
sobjects: ["Account", "Contact", "Case", "User"],
|
|
57
|
+
// Reads SF_LOGIN_URL, SF_CLIENT_ID and SF_CLIENT_SECRET from the environment.
|
|
58
|
+
auth: { type: "clientCredentials" },
|
|
59
|
+
picklists: "union",
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Generate types for your org
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx sobjectly generate --env-file .env
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 3. Use the typed client
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { SalesforceClient, clientCredentials } from "@cerios/salesforce-sobjectly";
|
|
73
|
+
import { API_VERSION, type SObjectRegistry } from "./generated/sobjects";
|
|
74
|
+
|
|
75
|
+
const sf = new SalesforceClient<SObjectRegistry>({
|
|
76
|
+
apiVersion: API_VERSION, // the same version the types were generated with
|
|
77
|
+
auth: clientCredentials({
|
|
78
|
+
loginUrl: process.env.SF_LOGIN_URL!, // your My Domain URL
|
|
79
|
+
clientId: process.env.SF_CLIENT_ID!,
|
|
80
|
+
clientSecret: process.env.SF_CLIENT_SECRET!,
|
|
81
|
+
}),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// CRUD
|
|
85
|
+
const accountId = await sf.sobject("Account").create({ Name: "Acme" });
|
|
86
|
+
await sf.sobject("Account").update(accountId, { Phone: "+31 20 123 4567" });
|
|
87
|
+
const account = await sf.sobject("Account").get(accountId, ["Id", "Name", "Phone"]);
|
|
88
|
+
|
|
89
|
+
// Typed SOQL
|
|
90
|
+
const page = await sf.query(
|
|
91
|
+
sf
|
|
92
|
+
.soql("Contact")
|
|
93
|
+
.select("Id", "Email")
|
|
94
|
+
.selectRelated("Account", "Name")
|
|
95
|
+
.where("Email", "LIKE", "%@acme.com")
|
|
96
|
+
.orderBy("LastName")
|
|
97
|
+
.limit(50),
|
|
98
|
+
);
|
|
99
|
+
page.records[0].Account?.Name; // string
|
|
100
|
+
page.records[0].Phone; // ❌ compile error: Phone was not selected
|
|
101
|
+
|
|
102
|
+
// Every record, across all pages
|
|
103
|
+
for await (const contact of sf.iterate(sf.soql("Contact").select("Id", "Email"))) {
|
|
104
|
+
console.log(contact.Email);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## 📖 Documentation
|
|
109
|
+
|
|
110
|
+
| Guide | What's in it |
|
|
111
|
+
| ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
|
|
112
|
+
| [Authentication](docs/authentication.md) | Salesforce CLI, access tokens, client credentials, JWT bearer, refresh tokens, custom providers |
|
|
113
|
+
| [Code generation](docs/codegen.md) | Config reference, type mapping, required fields, picklists, CI usage |
|
|
114
|
+
| [SOQL builder](docs/soql.md) | Selecting, relationships, filters, aggregates, pagination, escaping |
|
|
115
|
+
| [Composite & collections](docs/composite-and-collections.md) | Composite with references, batch, tree, graph, sObject collections |
|
|
116
|
+
| [Approvals, files, reports & more](docs/platform.md) | Record types, picklists, platform events, approvals, quick actions, UI API, files, reports, query plans |
|
|
117
|
+
| [Bulk API 2.0](docs/bulk.md) | Ingest jobs, query jobs, CSV handling |
|
|
118
|
+
| [Transports](docs/transports.md) | Custom HTTP clients (axios and Playwright examples), streaming, hooks, retries, timeouts |
|
|
119
|
+
| [Endpoint coverage](docs/endpoints.md) | Which Salesforce REST resources have typed helpers |
|
|
120
|
+
|
|
121
|
+
## 🧭 API Overview
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
sf.sobject("Account"): basicInfo, describe, create, get, update, delete, upsert, getByExternalId,
|
|
125
|
+
getDeleted, getUpdated, getBlob, query, collect, iterate
|
|
126
|
+
sf.soql("Account") → SoqlQueryBuilder
|
|
127
|
+
sf.query / sf.queryMore / sf.iterate / sf.collect (queryAll via { includeDeleted: true })
|
|
128
|
+
sf.search: sosl, parameterized, suggestions
|
|
129
|
+
sf.composite: execute, batch, tree, graph
|
|
130
|
+
sf.collections: create, update, upsert, delete, retrieve (≤ 200 per call, or { chunk: true })
|
|
131
|
+
sf.actions: invokeFlow, invokeApex, invokeStandard, invokeCustom, list*/describe*
|
|
132
|
+
sf.tooling: query, sobject, executeAnonymous, runTestsSynchronous, runTestsAsynchronous, request
|
|
133
|
+
sf.bulk: ingest, createIngestJob, query, createQueryJob
|
|
134
|
+
sf.events: publish (platform events)
|
|
135
|
+
sf.approvals: list, submit, approve, reject, pending
|
|
136
|
+
sf.files: upload, download, newVersion, link
|
|
137
|
+
sf.reports: list, describe, run, runAsync, instance, waitForInstance, toRows
|
|
138
|
+
sf.uiApi: objectInfo, picklistValues, record, layout
|
|
139
|
+
sf.quickActions / sf.sobject(name).quickActions: list, describe, defaultValues, invoke
|
|
140
|
+
sf.sobject(name).recordTypeId(), .picklistValues(); sf.explain(query); sf.clearCache()
|
|
141
|
+
sf.tooling.debugLogs: capture, list, body (executeAnonymous(apex, { captureLog: true }))
|
|
142
|
+
sf.apexRest(), sf.request(), sf.limits(), sf.versions(), sf.describeGlobal(), sf.recordCount()
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Flows and invocable actions
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
const [result] = await sf.actions.invokeFlow<{ accountId: string }, { caseId: string }>("Start_Onboarding", [
|
|
149
|
+
{ accountId },
|
|
150
|
+
]);
|
|
151
|
+
result.outputValues?.caseId;
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Composite with references
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
const result = await sf.composite.execute(
|
|
158
|
+
(c) => {
|
|
159
|
+
const account = c.create("Account", { Name: "Acme" });
|
|
160
|
+
const contact = c.create("Contact", { LastName: "Doe", AccountId: account.ref("id") });
|
|
161
|
+
return { account, contact };
|
|
162
|
+
},
|
|
163
|
+
{ allOrNone: true },
|
|
164
|
+
);
|
|
165
|
+
const contactId = result.get(result.refs.contact).id;
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Any other endpoint
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import type { OrgLimits } from "@cerios/salesforce-sobjectly";
|
|
172
|
+
|
|
173
|
+
const limits = await sf.request<OrgLimits>({ path: "/limits" });
|
|
174
|
+
const tooling = await sf.request({ path: "/tooling/query", query: { q: "SELECT Id FROM ApexClass" } });
|
|
175
|
+
const custom = await sf.apexRest<{ ok: boolean }>({ method: "POST", path: "/orders", body: { id: 1 } });
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`path` is relative to `/services/data/{apiVersion}`. Paths that start with `/services/` are used as-is, and absolute URLs must be on your instance's origin.
|
|
179
|
+
|
|
180
|
+
## 🔢 API version
|
|
181
|
+
|
|
182
|
+
There is no default API version: you always choose one.
|
|
183
|
+
|
|
184
|
+
- **Codegen:** `apiVersion` is required in `sobjectly.config.ts`, and `sobjectly generate` fails without it.
|
|
185
|
+
- **Client:** `apiVersion` is required in `new SalesforceClient({ ... })`, both as a TypeScript type and at runtime.
|
|
186
|
+
|
|
187
|
+
A fixed version means a Salesforce release, or an update of this package, never silently changes the API contract you call. Salesforce freezes behaviour per API version.
|
|
188
|
+
|
|
189
|
+
The generated file exports the version it was generated with as `API_VERSION`. Pass that to the client, so your types and your requests always use the same version. To upgrade, change `apiVersion` in the config and run `sobjectly generate` again.
|
|
190
|
+
|
|
191
|
+
## 🧯 Errors
|
|
192
|
+
|
|
193
|
+
| Error | When |
|
|
194
|
+
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
195
|
+
| `SalesforceError` | Any non-2xx response. Carries `status`, `errorCode`, `errors[]`, `body`, `limitInfo` and a path without the query string. |
|
|
196
|
+
| `SalesforceSaveError` | A 2xx response in which an item reported `success: false` (create, collections, actions). |
|
|
197
|
+
| `SalesforcePartialFailureError` | A call split into several requests (chunked collections, batched events) failed after earlier chunks succeeded. `completedResults` holds their results; `cause` is the original error. |
|
|
198
|
+
| `SalesforceAuthError` | The token request failed. Carries the OAuth `error` and `errorDescription`, never secrets. |
|
|
199
|
+
| `SalesforceBulkJobError` | A Bulk job ended as `Failed` or `Aborted`, or timed out. |
|
|
200
|
+
| `ApexExecutionError` | Anonymous Apex failed to compile or threw. |
|
|
201
|
+
| `DebugLogCaptureError` | The work passed to `sf.tooling.debugLogs.capture()` threw. `logs` holds the logs collected anyway; `cause` is the original error. |
|
|
202
|
+
|
|
203
|
+
Error codes are typed, with autocomplete for the common ones (`SalesforceErrorCode`). Check them with the guards:
|
|
204
|
+
|
|
205
|
+
```ts
|
|
206
|
+
import { hasErrorCode, isSalesforceError } from "@cerios/salesforce-sobjectly";
|
|
207
|
+
|
|
208
|
+
try {
|
|
209
|
+
await sf.sobject("Account").update(id, { Name: "Acme" });
|
|
210
|
+
} catch (error) {
|
|
211
|
+
if (hasErrorCode(error, "UNABLE_TO_LOCK_ROW")) {
|
|
212
|
+
// retry later
|
|
213
|
+
} else if (isSalesforceError(error, "INSUFFICIENT_ACCESS_OR_READONLY")) {
|
|
214
|
+
// ...
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## 🤝 Contributing
|
|
220
|
+
|
|
221
|
+
Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md). Report security issues as described in [SECURITY.md](SECURITY.md).
|
|
222
|
+
|
|
223
|
+
## 📄 License
|
|
224
|
+
|
|
225
|
+
MIT © Ronald Veth - Cerios
|
|
226
|
+
|
|
227
|
+
## 🔗 Links
|
|
228
|
+
|
|
229
|
+
- [GitHub repository](https://github.com/CeriosTesting/salesforce-sobjectly)
|
|
230
|
+
- [npm package](https://www.npmjs.com/package/@cerios/salesforce-sobjectly)
|
|
231
|
+
- [Issue tracker](https://github.com/CeriosTesting/salesforce-sobjectly/issues)
|
|
232
|
+
- [Changelog](CHANGELOG.md)
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const require_sf_cli = require("./sf-cli-DEVZQTR7.cjs");
|
|
3
|
+
const require_codegen = require("./codegen-BgDgU2RR.cjs");
|
|
4
|
+
let node_fs = require("node:fs");
|
|
5
|
+
let node_path = require("node:path");
|
|
6
|
+
let node_util = require("node:util");
|
|
7
|
+
//#region src/cli.ts
|
|
8
|
+
const HELP = `Usage: sobjectly <command> [options]
|
|
9
|
+
|
|
10
|
+
Commands:
|
|
11
|
+
init Create a sobjectly.config file by answering a few questions
|
|
12
|
+
generate Generate TypeScript types for Salesforce sObjects (default)
|
|
13
|
+
|
|
14
|
+
Options for generate:
|
|
15
|
+
-c, --config <path> Config file (default: sobjectly.config.ts or sobjectly.config.json)
|
|
16
|
+
-e, --env-file <path> Load environment variables from a .env file first
|
|
17
|
+
--check Don't write; exit with 1 when the generated file is out of date
|
|
18
|
+
(formatting-only differences don't count)
|
|
19
|
+
--stdout Print the generated source instead of writing it
|
|
20
|
+
|
|
21
|
+
Options for init (skip the matching questions; useful in scripts):
|
|
22
|
+
--api-version <v> Salesforce API version, e.g. v66.0
|
|
23
|
+
--output <path> Generated types file, e.g. src/generated/sobjects.ts
|
|
24
|
+
--sobjects <list> Comma-separated sObject API names
|
|
25
|
+
--auth <type> clientCredentials | sfCli | accessToken | jwtBearer
|
|
26
|
+
--target-org <alias> Salesforce CLI org alias (with --auth sfCli)
|
|
27
|
+
--picklists <mode> union | string
|
|
28
|
+
--format <format> ts | json
|
|
29
|
+
-y, --yes Use the suggested answer for every other question (needs --api-version)
|
|
30
|
+
-f, --force Replace an existing config without asking
|
|
31
|
+
|
|
32
|
+
-h, --help Show this help
|
|
33
|
+
`;
|
|
34
|
+
const options = {
|
|
35
|
+
config: {
|
|
36
|
+
type: "string",
|
|
37
|
+
short: "c"
|
|
38
|
+
},
|
|
39
|
+
"env-file": {
|
|
40
|
+
type: "string",
|
|
41
|
+
short: "e"
|
|
42
|
+
},
|
|
43
|
+
"api-version": { type: "string" },
|
|
44
|
+
output: { type: "string" },
|
|
45
|
+
sobjects: { type: "string" },
|
|
46
|
+
auth: { type: "string" },
|
|
47
|
+
picklists: { type: "string" },
|
|
48
|
+
format: { type: "string" },
|
|
49
|
+
check: { type: "boolean" },
|
|
50
|
+
stdout: { type: "boolean" },
|
|
51
|
+
"target-org": { type: "string" },
|
|
52
|
+
yes: {
|
|
53
|
+
type: "boolean",
|
|
54
|
+
short: "y"
|
|
55
|
+
},
|
|
56
|
+
force: {
|
|
57
|
+
type: "boolean",
|
|
58
|
+
short: "f"
|
|
59
|
+
},
|
|
60
|
+
help: {
|
|
61
|
+
type: "boolean",
|
|
62
|
+
short: "h"
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
async function main(argv) {
|
|
66
|
+
const { values, positionals } = (0, node_util.parseArgs)({
|
|
67
|
+
args: argv,
|
|
68
|
+
allowPositionals: true,
|
|
69
|
+
options
|
|
70
|
+
});
|
|
71
|
+
const command = positionals[0] ?? "generate";
|
|
72
|
+
if (values.help) {
|
|
73
|
+
console.log(HELP);
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
if (command === "init") return init(values);
|
|
77
|
+
if (command === "generate") return runGenerate(values);
|
|
78
|
+
console.error(`Unknown command "${command}".\n\n${HELP}`);
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
async function init(values) {
|
|
82
|
+
const preset = initPreset(values);
|
|
83
|
+
const prompter = require_codegen.readlinePrompter();
|
|
84
|
+
try {
|
|
85
|
+
await require_codegen.runInit({
|
|
86
|
+
cwd: process.cwd(),
|
|
87
|
+
prompter,
|
|
88
|
+
log: (message) => console.log(message),
|
|
89
|
+
preset,
|
|
90
|
+
yes: values.yes,
|
|
91
|
+
force: values.force
|
|
92
|
+
});
|
|
93
|
+
return 0;
|
|
94
|
+
} finally {
|
|
95
|
+
prompter.close();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function initPreset(values) {
|
|
99
|
+
const preset = {};
|
|
100
|
+
if (values["api-version"] !== void 0) {
|
|
101
|
+
const apiVersion = require_sf_cli.normalizeApiVersion(values["api-version"]);
|
|
102
|
+
if (!apiVersion) throw new Error(`Invalid --api-version "${values["api-version"]}". Expected a value like v66.0.`);
|
|
103
|
+
preset.apiVersion = apiVersion;
|
|
104
|
+
}
|
|
105
|
+
if (values.output !== void 0) preset.output = values.output;
|
|
106
|
+
if (values.sobjects !== void 0) preset.sobjects = require_codegen.parseSObjects(values.sobjects);
|
|
107
|
+
const auth = oneOf("--auth", values.auth, [
|
|
108
|
+
"clientCredentials",
|
|
109
|
+
"sfCli",
|
|
110
|
+
"accessToken",
|
|
111
|
+
"jwtBearer"
|
|
112
|
+
]);
|
|
113
|
+
if (values["target-org"] !== void 0) preset.targetOrg = values["target-org"];
|
|
114
|
+
const picklists = oneOf("--picklists", values.picklists, ["union", "string"]);
|
|
115
|
+
const format = oneOf("--format", values.format, ["ts", "json"]);
|
|
116
|
+
return {
|
|
117
|
+
...preset,
|
|
118
|
+
...auth && { auth },
|
|
119
|
+
...picklists && { picklists },
|
|
120
|
+
...format && { format }
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function oneOf(flag, value, allowed) {
|
|
124
|
+
if (value === void 0) return;
|
|
125
|
+
const match = allowed.find((item) => item === value);
|
|
126
|
+
if (!match) throw new Error(`Invalid ${flag} "${value}". Expected one of: ${allowed.join(", ")}.`);
|
|
127
|
+
return match;
|
|
128
|
+
}
|
|
129
|
+
async function runGenerate(values) {
|
|
130
|
+
if (values["env-file"]) {
|
|
131
|
+
const envFile = (0, node_path.resolve)(values["env-file"]);
|
|
132
|
+
if (!(0, node_fs.existsSync)(envFile)) {
|
|
133
|
+
console.error(`Env file not found: ${envFile}`);
|
|
134
|
+
return 1;
|
|
135
|
+
}
|
|
136
|
+
process.loadEnvFile(envFile);
|
|
137
|
+
}
|
|
138
|
+
const found = require_codegen.findConfigFiles();
|
|
139
|
+
if (!values.config && found.length > 1) console.warn(`warning: found ${found.join(" and ")}; using ${found[0]}. Remove the other one.`);
|
|
140
|
+
const configPath = values.config ? (0, node_path.resolve)(values.config) : found[0];
|
|
141
|
+
if (!configPath) {
|
|
142
|
+
console.error("No sobjectly config file found. Run \"npx sobjectly init\" to create one, or pass --config.");
|
|
143
|
+
return 1;
|
|
144
|
+
}
|
|
145
|
+
const { config, directory } = await require_codegen.loadConfig(configPath);
|
|
146
|
+
const logger = {
|
|
147
|
+
info: values.stdout ? (message) => console.error(message) : (message) => console.log(message),
|
|
148
|
+
warn: (message) => console.warn(`warning: ${message}`)
|
|
149
|
+
};
|
|
150
|
+
if (values.check) return reportCheck(await require_codegen.checkGenerated(config, {
|
|
151
|
+
cwd: directory,
|
|
152
|
+
logger
|
|
153
|
+
}));
|
|
154
|
+
const result = await require_codegen.generate(config, {
|
|
155
|
+
cwd: directory,
|
|
156
|
+
logger,
|
|
157
|
+
write: !values.stdout
|
|
158
|
+
});
|
|
159
|
+
if (values.stdout) process.stdout.write(result.source);
|
|
160
|
+
if (result.skipped.length > 0) console.warn(`Skipped ${result.skipped.length} sObject(s): ${result.skipped.map((item) => item.name).join(", ")}`);
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
function reportCheck(result) {
|
|
164
|
+
if (result.upToDate) {
|
|
165
|
+
console.log(`${result.outputPath} is up to date.`);
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
if (result.reason === "missing") {
|
|
169
|
+
console.error(`${result.outputPath} does not exist. Run "sobjectly generate".`);
|
|
170
|
+
return 1;
|
|
171
|
+
}
|
|
172
|
+
const details = [
|
|
173
|
+
result.added.length > 0 ? `added: ${result.added.join(", ")}` : "",
|
|
174
|
+
result.removed.length > 0 ? `removed: ${result.removed.join(", ")}` : "",
|
|
175
|
+
result.changed.length > 0 ? `changed: ${result.changed.join(", ")}` : ""
|
|
176
|
+
].filter((detail) => detail.length > 0);
|
|
177
|
+
console.error(`${result.outputPath} is out of date${details.length > 0 ? ` (${details.join("; ")})` : ""}. Run "sobjectly generate".`);
|
|
178
|
+
return 1;
|
|
179
|
+
}
|
|
180
|
+
main(process.argv.slice(2)).then((code) => {
|
|
181
|
+
process.exitCode = code;
|
|
182
|
+
}).catch((error) => {
|
|
183
|
+
console.error(error instanceof Error ? error.message : error);
|
|
184
|
+
process.exitCode = 1;
|
|
185
|
+
});
|
|
186
|
+
//#endregion
|
|
187
|
+
|
|
188
|
+
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.cjs","names":["parseArgs","readlinePrompter","runInit","normalizeApiVersion","parseSObjects","resolve","existsSync","findConfigFiles","loadConfig","checkGenerated","generate"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/* oxlint-disable no-console */\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\n\nimport { normalizeApiVersion } from \"./api-version\";\nimport { checkGenerated, findConfigFiles, generate, loadConfig } from \"./codegen/index\";\nimport { type InitAnswers, parseSObjects, runInit } from \"./codegen/init\";\nimport { readlinePrompter } from \"./codegen/prompter\";\n\nconst HELP = `Usage: sobjectly <command> [options]\n\nCommands:\n init Create a sobjectly.config file by answering a few questions\n generate Generate TypeScript types for Salesforce sObjects (default)\n\nOptions for generate:\n -c, --config <path> Config file (default: sobjectly.config.ts or sobjectly.config.json)\n -e, --env-file <path> Load environment variables from a .env file first\n --check Don't write; exit with 1 when the generated file is out of date\n (formatting-only differences don't count)\n --stdout Print the generated source instead of writing it\n\nOptions for init (skip the matching questions; useful in scripts):\n --api-version <v> Salesforce API version, e.g. v66.0\n --output <path> Generated types file, e.g. src/generated/sobjects.ts\n --sobjects <list> Comma-separated sObject API names\n --auth <type> clientCredentials | sfCli | accessToken | jwtBearer\n --target-org <alias> Salesforce CLI org alias (with --auth sfCli)\n --picklists <mode> union | string\n --format <format> ts | json\n -y, --yes Use the suggested answer for every other question (needs --api-version)\n -f, --force Replace an existing config without asking\n\n -h, --help Show this help\n`;\n\nconst options = {\n\tconfig: { type: \"string\", short: \"c\" },\n\t\"env-file\": { type: \"string\", short: \"e\" },\n\t\"api-version\": { type: \"string\" },\n\toutput: { type: \"string\" },\n\tsobjects: { type: \"string\" },\n\tauth: { type: \"string\" },\n\tpicklists: { type: \"string\" },\n\tformat: { type: \"string\" },\n\tcheck: { type: \"boolean\" },\n\tstdout: { type: \"boolean\" },\n\t\"target-org\": { type: \"string\" },\n\tyes: { type: \"boolean\", short: \"y\" },\n\tforce: { type: \"boolean\", short: \"f\" },\n\thelp: { type: \"boolean\", short: \"h\" },\n} as const;\n\ninterface Values {\n\tconfig?: string;\n\t\"env-file\"?: string;\n\t\"api-version\"?: string;\n\toutput?: string;\n\tsobjects?: string;\n\tauth?: string;\n\tpicklists?: string;\n\tformat?: string;\n\tcheck?: boolean;\n\tstdout?: boolean;\n\t\"target-org\"?: string;\n\tyes?: boolean;\n\tforce?: boolean;\n\thelp?: boolean;\n}\n\nasync function main(argv: string[]): Promise<number> {\n\tconst { values, positionals } = parseArgs({ args: argv, allowPositionals: true, options });\n\tconst command = positionals[0] ?? \"generate\";\n\tif (values.help) {\n\t\tconsole.log(HELP);\n\t\treturn 0;\n\t}\n\tif (command === \"init\") {\n\t\treturn init(values);\n\t}\n\tif (command === \"generate\") {\n\t\treturn runGenerate(values);\n\t}\n\tconsole.error(`Unknown command \"${command}\".\\n\\n${HELP}`);\n\treturn 1;\n}\n\nasync function init(values: Values): Promise<number> {\n\tconst preset = initPreset(values);\n\tconst prompter = readlinePrompter();\n\ttry {\n\t\tawait runInit({\n\t\t\tcwd: process.cwd(),\n\t\t\tprompter,\n\t\t\tlog: (message) => console.log(message),\n\t\t\tpreset,\n\t\t\tyes: values.yes,\n\t\t\tforce: values.force,\n\t\t});\n\t\treturn 0;\n\t} finally {\n\t\tprompter.close();\n\t}\n}\n\nfunction initPreset(values: Values): Partial<InitAnswers> {\n\tconst preset: Partial<InitAnswers> = {};\n\tif (values[\"api-version\"] !== undefined) {\n\t\tconst apiVersion = normalizeApiVersion(values[\"api-version\"]);\n\t\tif (!apiVersion) {\n\t\t\tthrow new Error(`Invalid --api-version \"${values[\"api-version\"]}\". Expected a value like v66.0.`);\n\t\t}\n\t\tpreset.apiVersion = apiVersion;\n\t}\n\tif (values.output !== undefined) {\n\t\tpreset.output = values.output;\n\t}\n\tif (values.sobjects !== undefined) {\n\t\tpreset.sobjects = parseSObjects(values.sobjects);\n\t}\n\tconst auth = oneOf(\"--auth\", values.auth, [\"clientCredentials\", \"sfCli\", \"accessToken\", \"jwtBearer\"] as const);\n\tif (values[\"target-org\"] !== undefined) {\n\t\tpreset.targetOrg = values[\"target-org\"];\n\t}\n\tconst picklists = oneOf(\"--picklists\", values.picklists, [\"union\", \"string\"] as const);\n\tconst format = oneOf(\"--format\", values.format, [\"ts\", \"json\"] as const);\n\treturn { ...preset, ...(auth && { auth }), ...(picklists && { picklists }), ...(format && { format }) };\n}\n\nfunction oneOf<T extends string>(flag: string, value: string | undefined, allowed: readonly T[]): T | undefined {\n\tif (value === undefined) {\n\t\treturn undefined;\n\t}\n\tconst match = allowed.find((item) => item === value);\n\tif (!match) {\n\t\tthrow new Error(`Invalid ${flag} \"${value}\". Expected one of: ${allowed.join(\", \")}.`);\n\t}\n\treturn match;\n}\n\nasync function runGenerate(values: Values): Promise<number> {\n\tif (values[\"env-file\"]) {\n\t\tconst envFile = resolve(values[\"env-file\"]);\n\t\tif (!existsSync(envFile)) {\n\t\t\tconsole.error(`Env file not found: ${envFile}`);\n\t\t\treturn 1;\n\t\t}\n\t\tprocess.loadEnvFile(envFile);\n\t}\n\tconst found = findConfigFiles();\n\tif (!values.config && found.length > 1) {\n\t\tconsole.warn(`warning: found ${found.join(\" and \")}; using ${found[0]}. Remove the other one.`);\n\t}\n\tconst configPath = values.config ? resolve(values.config) : found[0];\n\tif (!configPath) {\n\t\tconsole.error('No sobjectly config file found. Run \"npx sobjectly init\" to create one, or pass --config.');\n\t\treturn 1;\n\t}\n\tconst { config, directory } = await loadConfig(configPath);\n\t// With --stdout the source goes to stdout, so progress goes to stderr.\n\tconst log = values.stdout\n\t\t? (message: string): void => console.error(message)\n\t\t: (message: string): void => console.log(message);\n\tconst logger = { info: log, warn: (message: string): void => console.warn(`warning: ${message}`) };\n\tif (values.check) {\n\t\treturn reportCheck(await checkGenerated(config, { cwd: directory, logger }));\n\t}\n\tconst result = await generate(config, { cwd: directory, logger, write: !values.stdout });\n\tif (values.stdout) {\n\t\tprocess.stdout.write(result.source);\n\t}\n\tif (result.skipped.length > 0) {\n\t\tconsole.warn(`Skipped ${result.skipped.length} sObject(s): ${result.skipped.map((item) => item.name).join(\", \")}`);\n\t}\n\treturn 0;\n}\n\nfunction reportCheck(result: Awaited<ReturnType<typeof checkGenerated>>): number {\n\tif (result.upToDate) {\n\t\tconsole.log(`${result.outputPath} is up to date.`);\n\t\treturn 0;\n\t}\n\tif (result.reason === \"missing\") {\n\t\tconsole.error(`${result.outputPath} does not exist. Run \"sobjectly generate\".`);\n\t\treturn 1;\n\t}\n\tconst details = [\n\t\tresult.added.length > 0 ? `added: ${result.added.join(\", \")}` : \"\",\n\t\tresult.removed.length > 0 ? `removed: ${result.removed.join(\", \")}` : \"\",\n\t\tresult.changed.length > 0 ? `changed: ${result.changed.join(\", \")}` : \"\",\n\t].filter((detail) => detail.length > 0);\n\tconsole.error(\n\t\t`${result.outputPath} is out of date${details.length > 0 ? ` (${details.join(\"; \")})` : \"\"}. Run \"sobjectly generate\".`,\n\t);\n\treturn 1;\n}\n\nmain(process.argv.slice(2))\n\t.then((code) => {\n\t\tprocess.exitCode = code;\n\t})\n\t.catch((error: unknown) => {\n\t\tconsole.error(error instanceof Error ? error.message : error);\n\t\tprocess.exitCode = 1;\n\t});\n"],"mappings":"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"}
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Lt as __commonJSMin, Mt as init_api_version, Pt as normalizeApiVersion } from "./sf-cli-CtWtdkJ0.mjs";
|
|
3
|
+
import { a as init_prompter, c as init_init, d as runInit, g as loadConfig, l as parseSObjects, m as findConfigFiles, n as generate, o as readlinePrompter, r as init_codegen, t as checkGenerated } from "./codegen-CSx0WAtK.mjs";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { parseArgs } from "node:util";
|
|
7
|
+
//#region src/cli.ts
|
|
8
|
+
var require_cli = /* @__PURE__ */ __commonJSMin((() => {
|
|
9
|
+
init_api_version();
|
|
10
|
+
init_codegen();
|
|
11
|
+
init_init();
|
|
12
|
+
init_prompter();
|
|
13
|
+
const HELP = `Usage: sobjectly <command> [options]
|
|
14
|
+
|
|
15
|
+
Commands:
|
|
16
|
+
init Create a sobjectly.config file by answering a few questions
|
|
17
|
+
generate Generate TypeScript types for Salesforce sObjects (default)
|
|
18
|
+
|
|
19
|
+
Options for generate:
|
|
20
|
+
-c, --config <path> Config file (default: sobjectly.config.ts or sobjectly.config.json)
|
|
21
|
+
-e, --env-file <path> Load environment variables from a .env file first
|
|
22
|
+
--check Don't write; exit with 1 when the generated file is out of date
|
|
23
|
+
(formatting-only differences don't count)
|
|
24
|
+
--stdout Print the generated source instead of writing it
|
|
25
|
+
|
|
26
|
+
Options for init (skip the matching questions; useful in scripts):
|
|
27
|
+
--api-version <v> Salesforce API version, e.g. v66.0
|
|
28
|
+
--output <path> Generated types file, e.g. src/generated/sobjects.ts
|
|
29
|
+
--sobjects <list> Comma-separated sObject API names
|
|
30
|
+
--auth <type> clientCredentials | sfCli | accessToken | jwtBearer
|
|
31
|
+
--target-org <alias> Salesforce CLI org alias (with --auth sfCli)
|
|
32
|
+
--picklists <mode> union | string
|
|
33
|
+
--format <format> ts | json
|
|
34
|
+
-y, --yes Use the suggested answer for every other question (needs --api-version)
|
|
35
|
+
-f, --force Replace an existing config without asking
|
|
36
|
+
|
|
37
|
+
-h, --help Show this help
|
|
38
|
+
`;
|
|
39
|
+
const options = {
|
|
40
|
+
config: {
|
|
41
|
+
type: "string",
|
|
42
|
+
short: "c"
|
|
43
|
+
},
|
|
44
|
+
"env-file": {
|
|
45
|
+
type: "string",
|
|
46
|
+
short: "e"
|
|
47
|
+
},
|
|
48
|
+
"api-version": { type: "string" },
|
|
49
|
+
output: { type: "string" },
|
|
50
|
+
sobjects: { type: "string" },
|
|
51
|
+
auth: { type: "string" },
|
|
52
|
+
picklists: { type: "string" },
|
|
53
|
+
format: { type: "string" },
|
|
54
|
+
check: { type: "boolean" },
|
|
55
|
+
stdout: { type: "boolean" },
|
|
56
|
+
"target-org": { type: "string" },
|
|
57
|
+
yes: {
|
|
58
|
+
type: "boolean",
|
|
59
|
+
short: "y"
|
|
60
|
+
},
|
|
61
|
+
force: {
|
|
62
|
+
type: "boolean",
|
|
63
|
+
short: "f"
|
|
64
|
+
},
|
|
65
|
+
help: {
|
|
66
|
+
type: "boolean",
|
|
67
|
+
short: "h"
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
async function main(argv) {
|
|
71
|
+
const { values, positionals } = parseArgs({
|
|
72
|
+
args: argv,
|
|
73
|
+
allowPositionals: true,
|
|
74
|
+
options
|
|
75
|
+
});
|
|
76
|
+
const command = positionals[0] ?? "generate";
|
|
77
|
+
if (values.help) {
|
|
78
|
+
console.log(HELP);
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
if (command === "init") return init(values);
|
|
82
|
+
if (command === "generate") return runGenerate(values);
|
|
83
|
+
console.error(`Unknown command "${command}".\n\n${HELP}`);
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
async function init(values) {
|
|
87
|
+
const preset = initPreset(values);
|
|
88
|
+
const prompter = readlinePrompter();
|
|
89
|
+
try {
|
|
90
|
+
await runInit({
|
|
91
|
+
cwd: process.cwd(),
|
|
92
|
+
prompter,
|
|
93
|
+
log: (message) => console.log(message),
|
|
94
|
+
preset,
|
|
95
|
+
yes: values.yes,
|
|
96
|
+
force: values.force
|
|
97
|
+
});
|
|
98
|
+
return 0;
|
|
99
|
+
} finally {
|
|
100
|
+
prompter.close();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function initPreset(values) {
|
|
104
|
+
const preset = {};
|
|
105
|
+
if (values["api-version"] !== void 0) {
|
|
106
|
+
const apiVersion = normalizeApiVersion(values["api-version"]);
|
|
107
|
+
if (!apiVersion) throw new Error(`Invalid --api-version "${values["api-version"]}". Expected a value like v66.0.`);
|
|
108
|
+
preset.apiVersion = apiVersion;
|
|
109
|
+
}
|
|
110
|
+
if (values.output !== void 0) preset.output = values.output;
|
|
111
|
+
if (values.sobjects !== void 0) preset.sobjects = parseSObjects(values.sobjects);
|
|
112
|
+
const auth = oneOf("--auth", values.auth, [
|
|
113
|
+
"clientCredentials",
|
|
114
|
+
"sfCli",
|
|
115
|
+
"accessToken",
|
|
116
|
+
"jwtBearer"
|
|
117
|
+
]);
|
|
118
|
+
if (values["target-org"] !== void 0) preset.targetOrg = values["target-org"];
|
|
119
|
+
const picklists = oneOf("--picklists", values.picklists, ["union", "string"]);
|
|
120
|
+
const format = oneOf("--format", values.format, ["ts", "json"]);
|
|
121
|
+
return {
|
|
122
|
+
...preset,
|
|
123
|
+
...auth && { auth },
|
|
124
|
+
...picklists && { picklists },
|
|
125
|
+
...format && { format }
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function oneOf(flag, value, allowed) {
|
|
129
|
+
if (value === void 0) return;
|
|
130
|
+
const match = allowed.find((item) => item === value);
|
|
131
|
+
if (!match) throw new Error(`Invalid ${flag} "${value}". Expected one of: ${allowed.join(", ")}.`);
|
|
132
|
+
return match;
|
|
133
|
+
}
|
|
134
|
+
async function runGenerate(values) {
|
|
135
|
+
if (values["env-file"]) {
|
|
136
|
+
const envFile = resolve(values["env-file"]);
|
|
137
|
+
if (!existsSync(envFile)) {
|
|
138
|
+
console.error(`Env file not found: ${envFile}`);
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
process.loadEnvFile(envFile);
|
|
142
|
+
}
|
|
143
|
+
const found = findConfigFiles();
|
|
144
|
+
if (!values.config && found.length > 1) console.warn(`warning: found ${found.join(" and ")}; using ${found[0]}. Remove the other one.`);
|
|
145
|
+
const configPath = values.config ? resolve(values.config) : found[0];
|
|
146
|
+
if (!configPath) {
|
|
147
|
+
console.error("No sobjectly config file found. Run \"npx sobjectly init\" to create one, or pass --config.");
|
|
148
|
+
return 1;
|
|
149
|
+
}
|
|
150
|
+
const { config, directory } = await loadConfig(configPath);
|
|
151
|
+
const logger = {
|
|
152
|
+
info: values.stdout ? (message) => console.error(message) : (message) => console.log(message),
|
|
153
|
+
warn: (message) => console.warn(`warning: ${message}`)
|
|
154
|
+
};
|
|
155
|
+
if (values.check) return reportCheck(await checkGenerated(config, {
|
|
156
|
+
cwd: directory,
|
|
157
|
+
logger
|
|
158
|
+
}));
|
|
159
|
+
const result = await generate(config, {
|
|
160
|
+
cwd: directory,
|
|
161
|
+
logger,
|
|
162
|
+
write: !values.stdout
|
|
163
|
+
});
|
|
164
|
+
if (values.stdout) process.stdout.write(result.source);
|
|
165
|
+
if (result.skipped.length > 0) console.warn(`Skipped ${result.skipped.length} sObject(s): ${result.skipped.map((item) => item.name).join(", ")}`);
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
function reportCheck(result) {
|
|
169
|
+
if (result.upToDate) {
|
|
170
|
+
console.log(`${result.outputPath} is up to date.`);
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
if (result.reason === "missing") {
|
|
174
|
+
console.error(`${result.outputPath} does not exist. Run "sobjectly generate".`);
|
|
175
|
+
return 1;
|
|
176
|
+
}
|
|
177
|
+
const details = [
|
|
178
|
+
result.added.length > 0 ? `added: ${result.added.join(", ")}` : "",
|
|
179
|
+
result.removed.length > 0 ? `removed: ${result.removed.join(", ")}` : "",
|
|
180
|
+
result.changed.length > 0 ? `changed: ${result.changed.join(", ")}` : ""
|
|
181
|
+
].filter((detail) => detail.length > 0);
|
|
182
|
+
console.error(`${result.outputPath} is out of date${details.length > 0 ? ` (${details.join("; ")})` : ""}. Run "sobjectly generate".`);
|
|
183
|
+
return 1;
|
|
184
|
+
}
|
|
185
|
+
main(process.argv.slice(2)).then((code) => {
|
|
186
|
+
process.exitCode = code;
|
|
187
|
+
}).catch((error) => {
|
|
188
|
+
console.error(error instanceof Error ? error.message : error);
|
|
189
|
+
process.exitCode = 1;
|
|
190
|
+
});
|
|
191
|
+
}));
|
|
192
|
+
//#endregion
|
|
193
|
+
export default require_cli();
|
|
194
|
+
export {};
|
|
195
|
+
|
|
196
|
+
//# sourceMappingURL=cli.mjs.map
|