@decoupla/sdk 0.1.0 → 0.1.2
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 +128 -72
- package/dist/cli/index.cjs +246 -24
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +246 -24
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +246 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +30 -3
- package/dist/index.js +246 -24
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -8142,6 +8142,10 @@ var preloadType = z.lazy(
|
|
|
8142
8142
|
var requestSchema = z.object({
|
|
8143
8143
|
type: z.string().optional(),
|
|
8144
8144
|
op_type: z.enum(["get_entry", "get_entries", "inspect"]),
|
|
8145
|
+
// Which API surface to target when fetching entries. Defaults to 'live' in the client.
|
|
8146
|
+
api_type: z.enum(["live", "preview"]).optional(),
|
|
8147
|
+
// entry_id is required for get_entry operations (top-level param)
|
|
8148
|
+
entry_id: z.string().optional(),
|
|
8145
8149
|
filters: z.any().optional(),
|
|
8146
8150
|
preload: preloadType.optional(),
|
|
8147
8151
|
sort: z.array(z.tuple([z.string(), z.enum(["ASC", "DESC"])])).optional(),
|
|
@@ -8276,6 +8280,8 @@ function buildCreateFieldRequest(modelId, fieldName, fieldDef) {
|
|
|
8276
8280
|
control,
|
|
8277
8281
|
required: fieldDef.required ?? false,
|
|
8278
8282
|
description: (_b = fieldDef.settings) == null ? void 0 : _b.description,
|
|
8283
|
+
is_label: fieldDef.isLabel ?? false,
|
|
8284
|
+
options: fieldDef.options,
|
|
8279
8285
|
meta: Object.keys(meta).length > 0 ? meta : void 0
|
|
8280
8286
|
};
|
|
8281
8287
|
}
|
|
@@ -8293,6 +8299,12 @@ function buildUpdateFieldRequest(fieldId, changes) {
|
|
|
8293
8299
|
if (changes.description !== void 0) {
|
|
8294
8300
|
request.description = changes.description;
|
|
8295
8301
|
}
|
|
8302
|
+
if (changes.isLabel !== void 0) {
|
|
8303
|
+
request.is_label = !!changes.isLabel;
|
|
8304
|
+
}
|
|
8305
|
+
if (changes.options !== void 0) {
|
|
8306
|
+
request.options = changes.options;
|
|
8307
|
+
}
|
|
8296
8308
|
if (changes.references !== void 0) {
|
|
8297
8309
|
request.meta = { ...changes.meta || {}, reference_types: changes.references };
|
|
8298
8310
|
} else if (changes.meta !== void 0) {
|
|
@@ -8365,6 +8377,7 @@ var makeRequest = (options) => async (request) => {
|
|
|
8365
8377
|
const { apiToken, workspace } = options;
|
|
8366
8378
|
const {
|
|
8367
8379
|
op_type,
|
|
8380
|
+
api_type,
|
|
8368
8381
|
filters,
|
|
8369
8382
|
type,
|
|
8370
8383
|
preload,
|
|
@@ -8377,15 +8390,20 @@ var makeRequest = (options) => async (request) => {
|
|
|
8377
8390
|
acc.push({ field, direction });
|
|
8378
8391
|
return acc;
|
|
8379
8392
|
}, void 0);
|
|
8393
|
+
const outgoingApiType = op_type === "inspect" ? void 0 : api_type ?? "live";
|
|
8380
8394
|
const requestBody = {
|
|
8381
8395
|
op_type,
|
|
8382
8396
|
type,
|
|
8397
|
+
// pass through entry_id when present (used by get_entry)
|
|
8398
|
+
entry_id: request.entry_id,
|
|
8383
8399
|
filters,
|
|
8384
8400
|
preload,
|
|
8385
8401
|
sort: sendSort,
|
|
8386
8402
|
limit,
|
|
8387
8403
|
offset
|
|
8388
8404
|
};
|
|
8405
|
+
if (outgoingApiType)
|
|
8406
|
+
requestBody.api_type = outgoingApiType;
|
|
8389
8407
|
const req = await fetch(`${DECOUPLA_API_URL_BASE}${workspace}`, {
|
|
8390
8408
|
method: "POST",
|
|
8391
8409
|
headers: {
|
|
@@ -8422,7 +8440,7 @@ ${errorDetails}`);
|
|
|
8422
8440
|
return respData;
|
|
8423
8441
|
};
|
|
8424
8442
|
var getEntry = (request) => async (contentTypeDef, entryId, options) => {
|
|
8425
|
-
var _a;
|
|
8443
|
+
var _a, _b;
|
|
8426
8444
|
const normalizePreload = (p) => {
|
|
8427
8445
|
if (!Array.isArray(p))
|
|
8428
8446
|
return p;
|
|
@@ -8457,34 +8475,24 @@ var getEntry = (request) => async (contentTypeDef, entryId, options) => {
|
|
|
8457
8475
|
}
|
|
8458
8476
|
return out;
|
|
8459
8477
|
};
|
|
8478
|
+
const chosenApiType = (options == null ? void 0 : options.contentView) ?? "live";
|
|
8460
8479
|
const reqBody = {
|
|
8461
8480
|
op_type: "get_entry",
|
|
8462
8481
|
type: contentTypeDef.__definition.name,
|
|
8463
8482
|
entry_id: entryId,
|
|
8464
|
-
preload: normalizePreload((options == null ? void 0 : options.preload) || [])
|
|
8483
|
+
preload: normalizePreload((options == null ? void 0 : options.preload) || []),
|
|
8484
|
+
api_type: chosenApiType
|
|
8465
8485
|
};
|
|
8466
8486
|
try {
|
|
8467
8487
|
debug("[getEntry] request body:", JSON.stringify(reqBody));
|
|
8468
8488
|
} catch (e) {
|
|
8469
8489
|
}
|
|
8470
|
-
const resp = await
|
|
8471
|
-
method: "POST",
|
|
8472
|
-
headers: {
|
|
8473
|
-
"Content-Type": "application/json",
|
|
8474
|
-
"Authorization": `Bearer ${process.env.DECOUPLA_API_TOKEN}`
|
|
8475
|
-
},
|
|
8476
|
-
body: JSON.stringify(reqBody)
|
|
8477
|
-
});
|
|
8478
|
-
const respData = await resp.json();
|
|
8490
|
+
const resp = await request(reqBody);
|
|
8479
8491
|
try {
|
|
8480
|
-
debug("[getEntry] raw response:", JSON.stringify(
|
|
8492
|
+
debug("[getEntry] raw response:", JSON.stringify(resp, null, 2));
|
|
8481
8493
|
} catch (e) {
|
|
8482
8494
|
}
|
|
8483
|
-
|
|
8484
|
-
console.error("Get Entry Error:", respData);
|
|
8485
|
-
throw new Error(`API Error: ${respData.errors.map((e) => e.message).join(", ")}`);
|
|
8486
|
-
}
|
|
8487
|
-
const entry = (_a = respData.data) == null ? void 0 : _a.entry;
|
|
8495
|
+
const entry = ((_a = resp == null ? void 0 : resp.data) == null ? void 0 : _a.entry) || ((_b = resp == null ? void 0 : resp.data) == null ? void 0 : _b.node);
|
|
8488
8496
|
const normalizedEntry = {};
|
|
8489
8497
|
for (const [key, value] of Object.entries(entry || {})) {
|
|
8490
8498
|
normalizedEntry[snakeToCamel(key)] = value;
|
|
@@ -8506,8 +8514,10 @@ var getEntries = (request) => async (contentTypeDef, options) => {
|
|
|
8506
8514
|
limit,
|
|
8507
8515
|
offset,
|
|
8508
8516
|
preload = [],
|
|
8509
|
-
sort = []
|
|
8517
|
+
sort = [],
|
|
8518
|
+
contentView: optContentView
|
|
8510
8519
|
} = options;
|
|
8520
|
+
const api_type = optContentView ?? "live";
|
|
8511
8521
|
const normalizePreload = (p) => {
|
|
8512
8522
|
if (!Array.isArray(p))
|
|
8513
8523
|
return p;
|
|
@@ -8632,6 +8642,7 @@ var getEntries = (request) => async (contentTypeDef, options) => {
|
|
|
8632
8642
|
const reqBody = {
|
|
8633
8643
|
type: contentTypeDef.__definition.name,
|
|
8634
8644
|
op_type: "get_entries",
|
|
8645
|
+
api_type,
|
|
8635
8646
|
limit,
|
|
8636
8647
|
offset,
|
|
8637
8648
|
filters: backendFilters,
|
|
@@ -9151,20 +9162,153 @@ var normalizeEntryMetadata = (entry) => ({
|
|
|
9151
9162
|
createdAt: entry.created_at,
|
|
9152
9163
|
updatedAt: entry.updated_at
|
|
9153
9164
|
});
|
|
9154
|
-
var createEntry = (options) => async (contentTypeDef, fieldValues,
|
|
9155
|
-
var _a;
|
|
9165
|
+
var createEntry = (options) => async (contentTypeDef, fieldValues, optionsParam) => {
|
|
9166
|
+
var _a, _b, _c;
|
|
9156
9167
|
const { apiToken, workspace } = options;
|
|
9168
|
+
const opts = typeof optionsParam === "boolean" ? { published: optionsParam } : optionsParam || {};
|
|
9169
|
+
const published = opts.published ?? true;
|
|
9157
9170
|
const validation = validateFieldValues(fieldValues);
|
|
9158
9171
|
if (!validation.valid) {
|
|
9159
9172
|
throw new Error(validation.error || "Invalid field values");
|
|
9160
9173
|
}
|
|
9161
9174
|
const normalizedFieldValues = normalizeFieldValues(fieldValues);
|
|
9175
|
+
try {
|
|
9176
|
+
const fieldDefs = ((_a = contentTypeDef.__definition) == null ? void 0 : _a.fields) || {};
|
|
9177
|
+
const keyMap = /* @__PURE__ */ new Map();
|
|
9178
|
+
for (const defKey of Object.keys(fieldDefs)) {
|
|
9179
|
+
keyMap.set(defKey, defKey);
|
|
9180
|
+
try {
|
|
9181
|
+
keyMap.set(camelToSnake(defKey), defKey);
|
|
9182
|
+
} catch (e) {
|
|
9183
|
+
}
|
|
9184
|
+
try {
|
|
9185
|
+
keyMap.set(snakeToCamel(camelToSnake(defKey)), defKey);
|
|
9186
|
+
} catch (e) {
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
for (const [k, v] of Object.entries(normalizedFieldValues)) {
|
|
9190
|
+
const defKey = keyMap.get(k) || void 0;
|
|
9191
|
+
if (!defKey)
|
|
9192
|
+
continue;
|
|
9193
|
+
const fdef = fieldDefs[defKey];
|
|
9194
|
+
if (!fdef)
|
|
9195
|
+
continue;
|
|
9196
|
+
if (fdef.type === "date") {
|
|
9197
|
+
const toDateOnly = (val) => {
|
|
9198
|
+
if (val instanceof Date) {
|
|
9199
|
+
const y = val.getFullYear();
|
|
9200
|
+
const m = String(val.getMonth() + 1).padStart(2, "0");
|
|
9201
|
+
const d = String(val.getDate()).padStart(2, "0");
|
|
9202
|
+
return `${y}-${m}-${d}`;
|
|
9203
|
+
}
|
|
9204
|
+
if (typeof val === "string") {
|
|
9205
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(val))
|
|
9206
|
+
return val;
|
|
9207
|
+
const parsed = new Date(val);
|
|
9208
|
+
if (!isNaN(parsed.getTime())) {
|
|
9209
|
+
const y = parsed.getFullYear();
|
|
9210
|
+
const m = String(parsed.getMonth() + 1).padStart(2, "0");
|
|
9211
|
+
const d = String(parsed.getDate()).padStart(2, "0");
|
|
9212
|
+
return `${y}-${m}-${d}`;
|
|
9213
|
+
}
|
|
9214
|
+
}
|
|
9215
|
+
return val;
|
|
9216
|
+
};
|
|
9217
|
+
normalizedFieldValues[k] = toDateOnly(v);
|
|
9218
|
+
}
|
|
9219
|
+
}
|
|
9220
|
+
} catch (e) {
|
|
9221
|
+
}
|
|
9222
|
+
try {
|
|
9223
|
+
const fieldDefs = ((_b = contentTypeDef.__definition) == null ? void 0 : _b.fields) || {};
|
|
9224
|
+
const keyMap = /* @__PURE__ */ new Map();
|
|
9225
|
+
for (const defKey of Object.keys(fieldDefs)) {
|
|
9226
|
+
keyMap.set(defKey, defKey);
|
|
9227
|
+
try {
|
|
9228
|
+
keyMap.set(camelToSnake(defKey), defKey);
|
|
9229
|
+
} catch (e) {
|
|
9230
|
+
}
|
|
9231
|
+
try {
|
|
9232
|
+
keyMap.set(snakeToCamel(camelToSnake(defKey)), defKey);
|
|
9233
|
+
} catch (e) {
|
|
9234
|
+
}
|
|
9235
|
+
}
|
|
9236
|
+
for (const [k, v] of Object.entries(normalizedFieldValues)) {
|
|
9237
|
+
const defKey = keyMap.get(k) || keyMap.get(k) || void 0;
|
|
9238
|
+
if (!defKey)
|
|
9239
|
+
continue;
|
|
9240
|
+
const fdef = fieldDefs[defKey];
|
|
9241
|
+
if (!fdef)
|
|
9242
|
+
continue;
|
|
9243
|
+
if (fdef.type === "date") {
|
|
9244
|
+
const toDateOnly = (val) => {
|
|
9245
|
+
if (val instanceof Date) {
|
|
9246
|
+
const y = val.getFullYear();
|
|
9247
|
+
const m = String(val.getMonth() + 1).padStart(2, "0");
|
|
9248
|
+
const d = String(val.getDate()).padStart(2, "0");
|
|
9249
|
+
return `${y}-${m}-${d}`;
|
|
9250
|
+
}
|
|
9251
|
+
if (typeof val === "string") {
|
|
9252
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(val))
|
|
9253
|
+
return val;
|
|
9254
|
+
const parsed = new Date(val);
|
|
9255
|
+
if (!isNaN(parsed.getTime())) {
|
|
9256
|
+
const y = parsed.getFullYear();
|
|
9257
|
+
const m = String(parsed.getMonth() + 1).padStart(2, "0");
|
|
9258
|
+
const d = String(parsed.getDate()).padStart(2, "0");
|
|
9259
|
+
return `${y}-${m}-${d}`;
|
|
9260
|
+
}
|
|
9261
|
+
}
|
|
9262
|
+
return val;
|
|
9263
|
+
};
|
|
9264
|
+
normalizedFieldValues[k] = toDateOnly(v);
|
|
9265
|
+
}
|
|
9266
|
+
}
|
|
9267
|
+
} catch (e) {
|
|
9268
|
+
}
|
|
9269
|
+
const normalizePreload = (p) => {
|
|
9270
|
+
if (!Array.isArray(p))
|
|
9271
|
+
return p;
|
|
9272
|
+
const out = [];
|
|
9273
|
+
for (const item of p) {
|
|
9274
|
+
if (typeof item === "string") {
|
|
9275
|
+
out.push(camelToSnake(item));
|
|
9276
|
+
continue;
|
|
9277
|
+
}
|
|
9278
|
+
if (!Array.isArray(item)) {
|
|
9279
|
+
out.push(item);
|
|
9280
|
+
continue;
|
|
9281
|
+
}
|
|
9282
|
+
const [key, inner] = item;
|
|
9283
|
+
if (inner == null) {
|
|
9284
|
+
out.push(camelToSnake(key));
|
|
9285
|
+
continue;
|
|
9286
|
+
}
|
|
9287
|
+
let innerArr;
|
|
9288
|
+
if (typeof inner === "string") {
|
|
9289
|
+
innerArr = [inner];
|
|
9290
|
+
} else if (Array.isArray(inner)) {
|
|
9291
|
+
if (inner.length === 2 && typeof inner[0] === "string" && (Array.isArray(inner[1]) || inner[1] == null)) {
|
|
9292
|
+
innerArr = [inner];
|
|
9293
|
+
} else {
|
|
9294
|
+
innerArr = inner;
|
|
9295
|
+
}
|
|
9296
|
+
} else {
|
|
9297
|
+
innerArr = [inner];
|
|
9298
|
+
}
|
|
9299
|
+
out.push([camelToSnake(key), normalizePreload(innerArr)]);
|
|
9300
|
+
}
|
|
9301
|
+
return out;
|
|
9302
|
+
};
|
|
9162
9303
|
const requestBody = {
|
|
9163
9304
|
op_type: "create_entry",
|
|
9164
9305
|
type: contentTypeDef.__definition.name,
|
|
9165
9306
|
field_values: normalizedFieldValues,
|
|
9166
9307
|
published
|
|
9167
9308
|
};
|
|
9309
|
+
if (opts.preload) {
|
|
9310
|
+
requestBody.preload = normalizePreload(opts.preload);
|
|
9311
|
+
}
|
|
9168
9312
|
const response = await fetch(`${DECOUPLA_API_URL_BASE}${workspace}`, {
|
|
9169
9313
|
method: "POST",
|
|
9170
9314
|
headers: {
|
|
@@ -9180,15 +9324,24 @@ var createEntry = (options) => async (contentTypeDef, fieldValues, published = t
|
|
|
9180
9324
|
throw new Error(`Failed to create entry: ${errorMessages}`);
|
|
9181
9325
|
}
|
|
9182
9326
|
const createResponse = respData;
|
|
9183
|
-
const entry = (
|
|
9327
|
+
const entry = (_c = createResponse.data) == null ? void 0 : _c.entry;
|
|
9184
9328
|
if (!entry) {
|
|
9185
9329
|
throw new Error(
|
|
9186
9330
|
`Failed to create entry: Content type "${contentTypeDef.__definition.name}" returned null. This typically means the content type has no fields defined. Please add at least one field to the content type before creating entries.`
|
|
9187
9331
|
);
|
|
9188
9332
|
}
|
|
9333
|
+
if (opts.preload) {
|
|
9334
|
+
const normalized = { id: entry.id };
|
|
9335
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
9336
|
+
if (key !== "id") {
|
|
9337
|
+
normalized[snakeToCamel(key)] = value;
|
|
9338
|
+
}
|
|
9339
|
+
}
|
|
9340
|
+
return { data: normalized };
|
|
9341
|
+
}
|
|
9189
9342
|
return normalizeEntryMetadata(entry);
|
|
9190
9343
|
};
|
|
9191
|
-
var updateEntry = (options) => async (contentTypeDef, entryId, fieldValues,
|
|
9344
|
+
var updateEntry = (options) => async (contentTypeDef, entryId, fieldValues, optionsParam) => {
|
|
9192
9345
|
var _a;
|
|
9193
9346
|
const { apiToken, workspace } = options;
|
|
9194
9347
|
if (!isValidUUID(entryId)) {
|
|
@@ -9204,8 +9357,46 @@ var updateEntry = (options) => async (contentTypeDef, entryId, fieldValues, publ
|
|
|
9204
9357
|
entry_id: entryId,
|
|
9205
9358
|
field_values: normalizedFieldValues
|
|
9206
9359
|
};
|
|
9207
|
-
|
|
9208
|
-
|
|
9360
|
+
const opts = typeof optionsParam === "boolean" ? { published: optionsParam } : optionsParam || {};
|
|
9361
|
+
if (opts.published !== void 0) {
|
|
9362
|
+
requestBody.published = opts.published;
|
|
9363
|
+
}
|
|
9364
|
+
const normalizePreload = (p) => {
|
|
9365
|
+
if (!Array.isArray(p))
|
|
9366
|
+
return p;
|
|
9367
|
+
const out = [];
|
|
9368
|
+
for (const item of p) {
|
|
9369
|
+
if (typeof item === "string") {
|
|
9370
|
+
out.push(camelToSnake(item));
|
|
9371
|
+
continue;
|
|
9372
|
+
}
|
|
9373
|
+
if (!Array.isArray(item)) {
|
|
9374
|
+
out.push(item);
|
|
9375
|
+
continue;
|
|
9376
|
+
}
|
|
9377
|
+
const [key, inner] = item;
|
|
9378
|
+
if (inner == null) {
|
|
9379
|
+
out.push(camelToSnake(key));
|
|
9380
|
+
continue;
|
|
9381
|
+
}
|
|
9382
|
+
let innerArr;
|
|
9383
|
+
if (typeof inner === "string") {
|
|
9384
|
+
innerArr = [inner];
|
|
9385
|
+
} else if (Array.isArray(inner)) {
|
|
9386
|
+
if (inner.length === 2 && typeof inner[0] === "string" && (Array.isArray(inner[1]) || inner[1] == null)) {
|
|
9387
|
+
innerArr = [inner];
|
|
9388
|
+
} else {
|
|
9389
|
+
innerArr = inner;
|
|
9390
|
+
}
|
|
9391
|
+
} else {
|
|
9392
|
+
innerArr = [inner];
|
|
9393
|
+
}
|
|
9394
|
+
out.push([camelToSnake(key), normalizePreload(innerArr)]);
|
|
9395
|
+
}
|
|
9396
|
+
return out;
|
|
9397
|
+
};
|
|
9398
|
+
if (opts.preload) {
|
|
9399
|
+
requestBody.preload = normalizePreload(opts.preload);
|
|
9209
9400
|
}
|
|
9210
9401
|
debug(`[DEBUG] Update Entry Request:`, JSON.stringify(requestBody, null, 2));
|
|
9211
9402
|
const response = await fetch(`${DECOUPLA_API_URL_BASE}${workspace}`, {
|
|
@@ -9343,6 +9534,37 @@ var createClient = (config) => {
|
|
|
9343
9534
|
getEntries: getEntries(request),
|
|
9344
9535
|
// Note: inline preload literal inference is supported by getEntries overloads.
|
|
9345
9536
|
inspect: inspect(request),
|
|
9537
|
+
/**
|
|
9538
|
+
* Validate whether the current token can read the requested content view.
|
|
9539
|
+
* Returns true when the view is accessible, false when an authorization error is returned.
|
|
9540
|
+
* This helper inspects the remote for a content type and issues a harmless get_entries
|
|
9541
|
+
* against that content type using the requested view; it treats a structured
|
|
9542
|
+
* `{ errors: [{ field: 'authorization', ... }] }` as a permission failure.
|
|
9543
|
+
*/
|
|
9544
|
+
validateContentView: async (view) => {
|
|
9545
|
+
var _a, _b;
|
|
9546
|
+
try {
|
|
9547
|
+
const inspectResp = await inspect(request)();
|
|
9548
|
+
const firstCT = inspectResp.data.content_types && inspectResp.data.content_types[0];
|
|
9549
|
+
if (!firstCT)
|
|
9550
|
+
return true;
|
|
9551
|
+
const typeName = firstCT.slug || firstCT.id;
|
|
9552
|
+
const resp = await request({ op_type: "get_entries", type: typeName, limit: 1, api_type: view }).catch((err) => ({ __err: err }));
|
|
9553
|
+
if (resp == null ? void 0 : resp.__err) {
|
|
9554
|
+
throw resp.__err;
|
|
9555
|
+
}
|
|
9556
|
+
if (resp.errors || ((_a = resp.data) == null ? void 0 : _a.errors)) {
|
|
9557
|
+
const errors = resp.errors || ((_b = resp.data) == null ? void 0 : _b.errors) || [];
|
|
9558
|
+
return !errors.some((e) => e.field === "authorization");
|
|
9559
|
+
}
|
|
9560
|
+
return true;
|
|
9561
|
+
} catch (e) {
|
|
9562
|
+
if (e && typeof e === "object" && e.errors) {
|
|
9563
|
+
return !e.errors.some((er) => er.field === "authorization");
|
|
9564
|
+
}
|
|
9565
|
+
throw e;
|
|
9566
|
+
}
|
|
9567
|
+
},
|
|
9346
9568
|
sync: sync(request),
|
|
9347
9569
|
syncWithFields: syncWithFieldsBound,
|
|
9348
9570
|
upload: upload({ apiToken, workspace }),
|