@clivly/sdk 0.9.7 → 0.10.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 CHANGED
@@ -460,7 +460,7 @@ is honoured. Other `4xx` responses return immediately.
460
460
  | `retry.maxAttempts` / `retry.baseDelayMs` | `number` | `3` / `500` | |
461
461
  | `syncTrigger.path` / `.url` / `.secret` | `string` | — | See above |
462
462
  | `verifyToken` | `string` | — | For `createAuthVerifyHandler`. **Nothing calls that endpoint today** |
463
- | `introspector` | `NamespaceIntrospector` | — | `drizzleIntrospector(db)`. Used only by `doctor()` |
463
+ | `introspector` | `NamespaceIntrospector` | — | `drizzleIntrospector(db)`. Used by `doctor()`, derived-object previews, and dashboard-authored derived-object sync |
464
464
  | `namespaceReady` | `boolean` | `false` | Self-reported; no live check |
465
465
  | `env` | `Record<string, string \| undefined>` | `process.env` | For trigger-URL discovery in non-Node runtimes |
466
466
  | `framework` / `orm` / `sdkVersion` | `string` | — | Reported for dashboard display |
package/dist/index.cjs CHANGED
@@ -1559,6 +1559,24 @@ function timingSafeEqual(a, b) {
1559
1559
  }
1560
1560
  const RUN_ID_HEADER = "x-clivly-run-id";
1561
1561
  const SIGNATURE_HEADER = "x-clivly-signature";
1562
+ /**
1563
+ * An error the mounted trigger route should answer with a specific HTTP
1564
+ * status, rather than the generic 500 it uses for a genuinely unhandled
1565
+ * error. A fail-safe rejection (e.g. an unresolvable sample `entityKey`) is a
1566
+ * configuration mismatch the caller can fix — surfacing it as "your app
1567
+ * crashed" sends a developer hunting through their own sync handler instead.
1568
+ */
1569
+ var TriggerRequestError = class extends Error {
1570
+ status;
1571
+ constructor(status, message) {
1572
+ super(message);
1573
+ this.name = new.target.name;
1574
+ this.status = status;
1575
+ }
1576
+ };
1577
+ function triggerErrorStatus(error) {
1578
+ return error instanceof TriggerRequestError ? error.status : 500;
1579
+ }
1562
1580
  const SIGNATURE_TOLERANCE_SECONDS = 300;
1563
1581
  const SIGNATURE_HEADER_RE = /^t=(\d+),v1=([0-9a-f]+)$/;
1564
1582
  const SECONDS_PER_MS = 1e3;
@@ -1884,7 +1902,7 @@ function createClivlySDK(config) {
1884
1902
  };
1885
1903
  const runSampleSync = async (entityKey, runId) => {
1886
1904
  const resolved = resolveSampleSource(config, entityKey);
1887
- if (!resolved) throw new Error(`Clivly sync: sample entityKey "${entityKey}" did not resolve to a configured source`);
1905
+ if (!resolved) throw new TriggerRequestError(400, `Clivly sync: no source is configured for entity "${entityKey}". The dashboard maps this table, but clivly.config declares no matching entity — add it to \`entities\`, or remove the mapping in Integrations → Mapping.`);
1888
1906
  lastPushFailure = null;
1889
1907
  if (!await syncOne(resolved.source, resolved.path, "delta", resolved.objectType, runId, SAMPLE_BATCH_SIZE, SAMPLE_BATCH_SIZE, false)) throw new Error(describePushFailure([resolved.source.entity]));
1890
1908
  };
@@ -2123,7 +2141,9 @@ function createClivlySDK(config) {
2123
2141
  const parseTriggerBody = (rawBody) => {
2124
2142
  if (!rawBody) return {};
2125
2143
  try {
2126
- return JSON.parse(rawBody);
2144
+ const parsed = JSON.parse(rawBody);
2145
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
2146
+ return parsed;
2127
2147
  } catch {
2128
2148
  return {};
2129
2149
  }
@@ -2156,7 +2176,7 @@ function createClivlySDK(config) {
2156
2176
  ok: false,
2157
2177
  runId,
2158
2178
  error: error instanceof Error ? error.message : String(error)
2159
- }, 500);
2179
+ }, triggerErrorStatus(error));
2160
2180
  }
2161
2181
  };
2162
2182
  const start = async () => {
package/dist/index.mjs CHANGED
@@ -1558,6 +1558,24 @@ function timingSafeEqual(a, b) {
1558
1558
  }
1559
1559
  const RUN_ID_HEADER = "x-clivly-run-id";
1560
1560
  const SIGNATURE_HEADER = "x-clivly-signature";
1561
+ /**
1562
+ * An error the mounted trigger route should answer with a specific HTTP
1563
+ * status, rather than the generic 500 it uses for a genuinely unhandled
1564
+ * error. A fail-safe rejection (e.g. an unresolvable sample `entityKey`) is a
1565
+ * configuration mismatch the caller can fix — surfacing it as "your app
1566
+ * crashed" sends a developer hunting through their own sync handler instead.
1567
+ */
1568
+ var TriggerRequestError = class extends Error {
1569
+ status;
1570
+ constructor(status, message) {
1571
+ super(message);
1572
+ this.name = new.target.name;
1573
+ this.status = status;
1574
+ }
1575
+ };
1576
+ function triggerErrorStatus(error) {
1577
+ return error instanceof TriggerRequestError ? error.status : 500;
1578
+ }
1561
1579
  const SIGNATURE_TOLERANCE_SECONDS = 300;
1562
1580
  const SIGNATURE_HEADER_RE = /^t=(\d+),v1=([0-9a-f]+)$/;
1563
1581
  const SECONDS_PER_MS = 1e3;
@@ -1883,7 +1901,7 @@ function createClivlySDK(config) {
1883
1901
  };
1884
1902
  const runSampleSync = async (entityKey, runId) => {
1885
1903
  const resolved = resolveSampleSource(config, entityKey);
1886
- if (!resolved) throw new Error(`Clivly sync: sample entityKey "${entityKey}" did not resolve to a configured source`);
1904
+ if (!resolved) throw new TriggerRequestError(400, `Clivly sync: no source is configured for entity "${entityKey}". The dashboard maps this table, but clivly.config declares no matching entity — add it to \`entities\`, or remove the mapping in Integrations → Mapping.`);
1887
1905
  lastPushFailure = null;
1888
1906
  if (!await syncOne(resolved.source, resolved.path, "delta", resolved.objectType, runId, SAMPLE_BATCH_SIZE, SAMPLE_BATCH_SIZE, false)) throw new Error(describePushFailure([resolved.source.entity]));
1889
1907
  };
@@ -2122,7 +2140,9 @@ function createClivlySDK(config) {
2122
2140
  const parseTriggerBody = (rawBody) => {
2123
2141
  if (!rawBody) return {};
2124
2142
  try {
2125
- return JSON.parse(rawBody);
2143
+ const parsed = JSON.parse(rawBody);
2144
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
2145
+ return parsed;
2126
2146
  } catch {
2127
2147
  return {};
2128
2148
  }
@@ -2155,7 +2175,7 @@ function createClivlySDK(config) {
2155
2175
  ok: false,
2156
2176
  runId,
2157
2177
  error: error instanceof Error ? error.message : String(error)
2158
- }, 500);
2178
+ }, triggerErrorStatus(error));
2159
2179
  }
2160
2180
  };
2161
2181
  const start = async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clivly/sdk",
3
- "version": "0.9.7",
3
+ "version": "0.10.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Clivly SDK — connect your app to Clivly Cloud (heartbeat, discovery, auth bridge).",
@@ -55,7 +55,7 @@
55
55
  "access": "public"
56
56
  },
57
57
  "dependencies": {
58
- "@clivly/core": "0.9.7"
58
+ "@clivly/core": "0.10.1"
59
59
  },
60
60
  "peerDependencies": {
61
61
  "drizzle-orm": ">=0.30 || >=1.0.0-rc.0",