@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/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 fetch(`${DECOUPLA_API_URL_BASE}${process.env.DECOUPLA_WORKSPACE}`, {
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(respData, null, 2));
8492
+ debug("[getEntry] raw response:", JSON.stringify(resp, null, 2));
8481
8493
  } catch (e) {
8482
8494
  }
8483
- if (respData.errors) {
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, published = true) => {
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 = (_a = createResponse.data) == null ? void 0 : _a.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, published) => {
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
- if (published !== void 0) {
9208
- requestBody.published = published;
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 }),