clivly 0.9.7 → 0.10.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/README.md +9 -9
- package/{adapter-C2eRbBfo.d.cts → adapter-1TN0zohk.d.cts} +1 -1
- package/{adapter-D5LRa-NA.d.mts → adapter-uxirB88i.d.mts} +1 -1
- package/{auth-adapter-Bjgo8LhN.d.mts → auth-adapter-CXTiW3Mb.d.cts} +1 -1
- package/{auth-adapter-DWzgl5Yq.d.cts → auth-adapter-DSuYwv1c.d.mts} +1 -1
- package/auth-better-auth.d.cts +1 -1
- package/auth-better-auth.d.mts +1 -1
- package/auth-clerk.d.cts +1 -1
- package/auth-clerk.d.mts +1 -1
- package/auth-workos.d.cts +1 -1
- package/auth-workos.d.mts +1 -1
- package/core-adapter.d.cts +1 -1
- package/core-adapter.d.mts +1 -1
- package/core-auth-adapter.d.cts +1 -1
- package/core-auth-adapter.d.mts +1 -1
- package/core-drizzle.d.cts +1 -1
- package/core-drizzle.d.mts +1 -1
- package/core-entity-config.cjs +1 -1
- package/core-entity-config.d.cts +1 -1
- package/core-entity-config.d.mts +1 -1
- package/core-entity-config.mjs +1 -1
- package/core-entity-heuristics.cjs +1 -0
- package/core-entity-heuristics.d.cts +3 -3
- package/core-entity-heuristics.d.mts +3 -3
- package/core-entity-heuristics.mjs +2 -2
- package/core-mapping-form.d.cts +1 -1
- package/core-mapping-form.d.mts +1 -1
- package/core-mappings-config.cjs +1 -1
- package/core-mappings-config.d.cts +1 -1
- package/core-mappings-config.d.mts +1 -1
- package/core-mappings-config.mjs +1 -1
- package/core-sync-engine.cjs +1 -1
- package/core-sync-engine.d.cts +1 -1
- package/core-sync-engine.d.mts +1 -1
- package/core-sync-engine.mjs +1 -1
- package/core-types.cjs +4 -0
- package/core-types.d.cts +2 -2
- package/core-types.d.mts +2 -2
- package/core-types.mjs +2 -1
- package/core-view-compiler.cjs +1 -1
- package/core-view-compiler.d.cts +1 -1
- package/core-view-compiler.d.mts +1 -1
- package/core-view-compiler.mjs +1 -1
- package/core.cjs +17 -15
- package/core.d.cts +10 -10
- package/core.d.mts +10 -10
- package/core.mjs +9 -8
- package/{drizzle-BmDDgAIF.d.mts → drizzle-DTOuGXEn.d.cts} +1 -1
- package/{drizzle-ixVV4pPN.d.cts → drizzle-SGapoly2.d.mts} +1 -1
- package/drizzle.cjs +2 -2
- package/drizzle.d.cts +3 -3
- package/drizzle.d.mts +3 -3
- package/drizzle.mjs +2 -2
- package/{entity-config-C8IIkrmX.mjs → entity-config-C4d3K9CP.mjs} +81 -4
- package/{entity-config-B2RA53nw.d.cts → entity-config-DNQAMbMS.d.cts} +27 -1
- package/{entity-config-B2RA53nw.d.mts → entity-config-DNQAMbMS.d.mts} +27 -1
- package/{entity-config-CPM-Tp-d.cjs → entity-config-DgAe0_YD.cjs} +86 -3
- package/index.cjs +24 -14
- package/index.mjs +24 -14
- package/internal-BAOG5eE4.d.mts +226 -0
- package/internal-BHJ0X3fC.d.cts +226 -0
- package/internal-Bgiimp5D.mjs +144 -0
- package/internal-Cz59xMj8.cjs +167 -0
- package/internal-adapter.d.cts +1 -1
- package/internal-adapter.d.mts +1 -1
- package/internal-auth-adapter.d.cts +1 -1
- package/internal-auth-adapter.d.mts +1 -1
- package/internal-drizzle.d.cts +1 -1
- package/internal-drizzle.d.mts +1 -1
- package/internal-entity-config.cjs +1 -1
- package/internal-entity-config.d.cts +1 -1
- package/internal-entity-config.d.mts +1 -1
- package/internal-entity-config.mjs +1 -1
- package/internal-entity-heuristics.cjs +34 -1
- package/internal-entity-heuristics.d.cts +8 -2
- package/internal-entity-heuristics.d.mts +8 -2
- package/internal-entity-heuristics.mjs +34 -2
- package/internal-mapping-form.d.cts +1 -1
- package/internal-mapping-form.d.mts +1 -1
- package/internal-mappings-config.cjs +1 -1
- package/internal-mappings-config.d.cts +1 -1
- package/internal-mappings-config.d.mts +1 -1
- package/internal-mappings-config.mjs +1 -1
- package/internal-sync-engine.cjs +1 -1
- package/internal-sync-engine.d.cts +1 -1
- package/internal-sync-engine.d.mts +1 -1
- package/internal-sync-engine.mjs +1 -1
- package/internal-types.cjs +4 -0
- package/internal-types.d.cts +2 -2
- package/internal-types.d.mts +2 -2
- package/internal-types.mjs +2 -1
- package/internal-view-compiler.cjs +1 -1
- package/internal-view-compiler.d.cts +1 -1
- package/internal-view-compiler.d.mts +1 -1
- package/internal-view-compiler.mjs +1 -1
- package/internal.cjs +17 -15
- package/internal.d.cts +10 -10
- package/internal.d.mts +10 -10
- package/internal.mjs +9 -8
- package/{mapping-form-DH77BXLv.d.mts → mapping-form-Cc7Ewv8c.d.mts} +1 -1
- package/{mapping-form-J0aU6O7l.d.cts → mapping-form-DwdbYAfR.d.cts} +1 -1
- package/{mappings-config-D9dsonoi.d.mts → mappings-config-Be9D43Np.d.mts} +1 -1
- package/{mappings-config-RQLGx8ub.d.cts → mappings-config-DUt4-9iO.d.cts} +1 -1
- package/{mappings-config-CBwL3FVh.cjs → mappings-config-Dx9ldgdf.cjs} +2 -16
- package/{mappings-config-DUKBe6OS.mjs → mappings-config-MoXBeI8p.mjs} +1 -15
- package/{namespace-probe-BQ4imnV7.d.mts → namespace-probe-9ScMDR0t.d.mts} +1 -1
- package/{namespace-probe-DZIGdvlm.d.cts → namespace-probe-DC4tbmLj.d.cts} +1 -1
- package/package.json +1 -1
- package/{projection-hnPSgH6G.mjs → projection-CSGJbZlZ.mjs} +1 -1
- package/{projection-DEwL-D8R.cjs → projection-Dz6_i1-w.cjs} +0 -6
- package/{projection-sql-iHy-Cx6V.cjs → projection-sql-CbXCKt3s.cjs} +1 -1
- package/{projection-sql-9zzTVGvf.mjs → projection-sql-iHoiYr1R.mjs} +1 -1
- package/{sdk-BL9BCzyD.cjs → sdk-B1vX4kUJ.cjs} +26 -6
- package/{sdk-CubnsyD9.mjs → sdk-D6gX0TXS.mjs} +26 -6
- package/sdk.cjs +2 -2
- package/sdk.d.cts +2 -2
- package/sdk.d.mts +2 -2
- package/sdk.mjs +2 -2
- package/{sync-engine-BLgwLpR6.cjs → sync-engine-B0FT1ax6.cjs} +1 -1
- package/{sync-engine-B_IzoUSx.mjs → sync-engine-CTSIcLlZ.mjs} +1 -1
- package/{sync-engine-BG2O1an2.d.cts → sync-engine-Df0kHAoJ.d.cts} +2 -2
- package/{sync-engine-DgQbrJON.d.mts → sync-engine-ugFacJ6G.d.mts} +2 -2
- package/{types-B1oRXdNP.d.mts → types-Cq1uHUKp.d.cts} +28 -2
- package/{types-B1oRXdNP.d.cts → types-Cq1uHUKp.d.mts} +28 -2
- package/types-Cq5gHCTq.cjs +20 -0
- package/types-YJTkXKzU.mjs +9 -0
- package/{view-compiler-D_6gfXFr.mjs → view-compiler-BzKk8nY3.mjs} +2 -2
- package/{view-compiler-Bo7pqXW3.d.mts → view-compiler-D3zp9S3K.d.cts} +1 -1
- package/{view-compiler-CmEdAMwj.d.cts → view-compiler-TpzDTu7z.d.mts} +1 -1
- package/{view-compiler-BHI5E4QI.cjs → view-compiler-ZKZbd3SK.cjs} +2 -2
- package/vite.d.cts +1 -1
- package/vite.d.mts +1 -1
- package/internal-BLKMnIIZ.cjs +0 -259
- package/internal-BpVh_J77.mjs +0 -212
- package/internal-CvlFk3KC.d.mts +0 -180
- package/internal-DGUOLQAq.d.cts +0 -180
package/drizzle.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_projection = require("./projection-
|
|
2
|
+
const require_projection = require("./projection-Dz6_i1-w.cjs");
|
|
3
3
|
const require_drizzle_meta = require("./drizzle-meta-Bb1JWYqf.cjs");
|
|
4
|
-
const require_projection_sql = require("./projection-sql-
|
|
4
|
+
const require_projection_sql = require("./projection-sql-CbXCKt3s.cjs");
|
|
5
5
|
const require_drizzle = require("./drizzle-DcB7OfBp.cjs");
|
|
6
6
|
let drizzle_orm = require("drizzle-orm");
|
|
7
7
|
//#region ../sdk/src/drizzle-projection.ts
|
package/drizzle.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { F as
|
|
2
|
-
import { n as buildUniqueConstraints, r as discoverFromDrizzle } from "./drizzle-
|
|
3
|
-
import { t as NamespaceIntrospector, v as SyncSource } from "./namespace-probe-
|
|
1
|
+
import { F as ProjectionMapping, I as ProjectionRef } from "./entity-config-DNQAMbMS.cjs";
|
|
2
|
+
import { n as buildUniqueConstraints, r as discoverFromDrizzle } from "./drizzle-DTOuGXEn.cjs";
|
|
3
|
+
import { t as NamespaceIntrospector, v as SyncSource } from "./namespace-probe-DC4tbmLj.cjs";
|
|
4
4
|
import { Table } from "drizzle-orm";
|
|
5
5
|
//#region ../sdk/src/drizzle-projection.d.ts
|
|
6
6
|
/**
|
package/drizzle.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { F as
|
|
2
|
-
import { n as buildUniqueConstraints, r as discoverFromDrizzle } from "./drizzle-
|
|
3
|
-
import { t as NamespaceIntrospector, v as SyncSource } from "./namespace-probe-
|
|
1
|
+
import { F as ProjectionMapping, I as ProjectionRef } from "./entity-config-DNQAMbMS.mjs";
|
|
2
|
+
import { n as buildUniqueConstraints, r as discoverFromDrizzle } from "./drizzle-SGapoly2.mjs";
|
|
3
|
+
import { t as NamespaceIntrospector, v as SyncSource } from "./namespace-probe-9ScMDR0t.mjs";
|
|
4
4
|
import { Table } from "drizzle-orm";
|
|
5
5
|
//#region ../sdk/src/drizzle-projection.d.ts
|
|
6
6
|
/**
|
package/drizzle.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as BASE_REF } from "./projection-
|
|
1
|
+
import { t as BASE_REF } from "./projection-CSGJbZlZ.mjs";
|
|
2
2
|
import { t as DRIZZLE_META } from "./drizzle-meta-D-5k3n0i.mjs";
|
|
3
|
-
import { a as compileProjectionPreview, i as compileProjectionJoins, n as compileProjectionFrom, r as compileProjectionIdentity, s as quoteIdent } from "./projection-sql-
|
|
3
|
+
import { a as compileProjectionPreview, i as compileProjectionJoins, n as compileProjectionFrom, r as compileProjectionIdentity, s as quoteIdent } from "./projection-sql-iHoiYr1R.mjs";
|
|
4
4
|
import { n as buildUniqueConstraints, r as discoverFromDrizzle, t as buildColumnsMeta } from "./drizzle-wbcuG-cy.mjs";
|
|
5
5
|
import { asc, getTableColumns, getTableName, sql } from "drizzle-orm";
|
|
6
6
|
//#region ../sdk/src/drizzle-projection.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as _enum, c as literal, d as strictObject, f as string, i as projectionSchema, l as number, o as array, p as union, r as identityRefs, s as boolean, t as BASE_REF, u as record } from "./projection-CSGJbZlZ.mjs";
|
|
2
2
|
//#region ../core/src/schema-meta.ts
|
|
3
3
|
/**
|
|
4
4
|
* Accessors over a discovered table's schema-v2 metadata.
|
|
@@ -29,7 +29,17 @@ function coversUniqueKey(table, columns) {
|
|
|
29
29
|
const present = new Set(columns);
|
|
30
30
|
return (table.uniqueConstraints ?? []).some((key) => key.length > 0 && key.every((column) => present.has(column)));
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Is this table's payload rich enough to validate a projection against?
|
|
34
|
+
*
|
|
35
|
+
* A type predicate rather than a plain boolean so the narrowing is visible to
|
|
36
|
+
* the compiler: callers that guard on this can iterate `columnsMeta` directly
|
|
37
|
+
* instead of casting, and a cast that outlives its guard becomes a type error
|
|
38
|
+
* rather than a silent walk over `undefined`.
|
|
39
|
+
*
|
|
40
|
+
* The invariant is unchanged: this reports whether the metadata is *known*,
|
|
41
|
+
* never whether the table has keys. Absent metadata stays UNKNOWN.
|
|
42
|
+
*/
|
|
33
43
|
function hasRichMeta(table) {
|
|
34
44
|
return Boolean(table.columnsMeta && table.uniqueConstraints);
|
|
35
45
|
}
|
|
@@ -273,6 +283,64 @@ function checkFanOut(errors, projection, resolver, base, basePk) {
|
|
|
273
283
|
});
|
|
274
284
|
}
|
|
275
285
|
/**
|
|
286
|
+
* One error naming a filter column that is not on the projection's base table,
|
|
287
|
+
* and the base table it would have to be on.
|
|
288
|
+
*/
|
|
289
|
+
function columnOnBase(column, baseTable, path) {
|
|
290
|
+
if (baseTable?.columns.includes(column)) return [];
|
|
291
|
+
return [{
|
|
292
|
+
path,
|
|
293
|
+
message: `filter column "${column}" does not exist on "${baseTable?.name ?? "the base table"}" — a derived object is filtered by its base table's columns only, so name one of those, or move the condition into a join's \`on\``
|
|
294
|
+
}];
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* A derived object's filter is evaluated in the cloud against pushed rows, so
|
|
298
|
+
* it can only name columns of the base table.
|
|
299
|
+
*
|
|
300
|
+
* Every rejected shape here fails *silently* today, which is the whole reason
|
|
301
|
+
* the check exists — a filter that cannot be honoured must error, never quietly
|
|
302
|
+
* return the wrong rows. The three shapes fail in different directions:
|
|
303
|
+
*
|
|
304
|
+
* - `$raw` is opaque SQL meant for the embedded model. The cloud path cannot
|
|
305
|
+
* evaluate it and passes every row, so the filter is ignored and the object
|
|
306
|
+
* syncs UNFILTERED.
|
|
307
|
+
* - A joined-table column resolves to nothing on a pushed row, so under a
|
|
308
|
+
* positive operator (`eq`, `in`) no row matches and the object syncs EMPTY.
|
|
309
|
+
* - The same unresolvable column under a NEGATIVE operator (`ne`, `isNull`,
|
|
310
|
+
* `notIn`) is the dangerous one: the missing cell reads as null, so the
|
|
311
|
+
* predicate is true for every row and a filter meant to narrow the sync
|
|
312
|
+
* ADMITS EVERYTHING. Nobody notices extra rows arriving.
|
|
313
|
+
*
|
|
314
|
+
* Rejecting an unresolvable column closes the last two together.
|
|
315
|
+
*/
|
|
316
|
+
function validateProjectionFilter(filter, projection, schema, base) {
|
|
317
|
+
if (!filter) return [];
|
|
318
|
+
const path = `${base}.filter`;
|
|
319
|
+
if ("$raw" in filter) return [{
|
|
320
|
+
path,
|
|
321
|
+
message: "a $raw filter cannot be evaluated against a derived object's rows — it is opaque SQL, so the cloud passes every row through unfiltered. Express it as column conditions on the base table instead"
|
|
322
|
+
}];
|
|
323
|
+
const baseTable = schema.find((t) => t.name === projection.baseTable);
|
|
324
|
+
const errors = [];
|
|
325
|
+
for (const column of Object.keys(filter)) {
|
|
326
|
+
if (column.includes(".")) {
|
|
327
|
+
const [ref, ...rest] = column.split(".");
|
|
328
|
+
const bare = rest.join(".");
|
|
329
|
+
if (ref !== "base") {
|
|
330
|
+
errors.push({
|
|
331
|
+
path,
|
|
332
|
+
message: `filter column "${column}" names the join "${ref}", but a derived object is filtered by its base table ("${projection.baseTable}") only — filter on a base column, or move the condition into that join's \`on\``
|
|
333
|
+
});
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
errors.push(...columnOnBase(bare, baseTable, path));
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
errors.push(...columnOnBase(column, baseTable, path));
|
|
340
|
+
}
|
|
341
|
+
return errors;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
276
344
|
* Validate a projection against the discovered schema. Returns every error
|
|
277
345
|
* found (not just the first) so a builder UI can show them all at once.
|
|
278
346
|
*/
|
|
@@ -537,6 +605,15 @@ function customObjectDeclarations(config) {
|
|
|
537
605
|
* Qualified rather than bare: two bindings can pull the same column name from
|
|
538
606
|
* different tables (`cl.full_name` and `adv.full_name`), which a bare column
|
|
539
607
|
* name cannot tell apart.
|
|
608
|
+
*
|
|
609
|
+
* This is the single derivation of that map, and both consumers depend on it
|
|
610
|
+
* emitting ref tokens. `mappingsToEntitiesConfig` populates a projection
|
|
611
|
+
* entity's required `fields`; the cloud ingest path needs the same map because
|
|
612
|
+
* a projection's persisted `crm_entity_mappings.field_map` is empty by design,
|
|
613
|
+
* so `rowToSourceRow` derives the effective field map from the projection
|
|
614
|
+
* instead. The tokens emitted here are exactly how `fromDrizzleProjection` keys
|
|
615
|
+
* the rows it pushes (`drizzle-projection.ts` `refToken`), so the two line up.
|
|
616
|
+
* Deriving this a second time anywhere would let the copies drift.
|
|
540
617
|
*/
|
|
541
618
|
function projectionFields(projection) {
|
|
542
619
|
const fields = {};
|
|
@@ -675,7 +752,7 @@ function validateEntity(v, config, key, entity, schema) {
|
|
|
675
752
|
if (entity.source !== entity.projection.baseTable) v.report(`${base}.source`, `"source" ("${entity.source}") must match "projection.baseTable" ("${entity.projection.baseTable}") — the view compiler reads baseTable and the sync engine reads source, so a mismatch makes them disagree about which table this entity is`);
|
|
676
753
|
for (const error of validateProjection(entity.projection, schema, base)) v.report(error.path, error.message);
|
|
677
754
|
validateReadOnly(v, base, entity);
|
|
678
|
-
|
|
755
|
+
for (const error of validateProjectionFilter(entity.filter, entity.projection, schema, base)) v.report(error.path, error.message);
|
|
679
756
|
return;
|
|
680
757
|
}
|
|
681
758
|
validateFields(v, base, entity);
|
|
@@ -701,4 +778,4 @@ function validateEntitiesConfig(config, schema) {
|
|
|
701
778
|
};
|
|
702
779
|
}
|
|
703
780
|
//#endregion
|
|
704
|
-
export {
|
|
781
|
+
export { joinFansOut as _, MATERIALIZING_CONCEPTS as a, hasRichMeta as b, entitiesConfigSchema as c, relationshipSchema as d, relationshipsSchema as f, fanOutJoins as g, validateProjectionFilter as h, ClivlyConfigError as i, filterSchema as l, validateProjection as m, CRM_CONCEPTS as n, customObjectDeclarations as o, validateEntitiesConfig as p, CUSTOM_CONCEPT as r, defineClivlyConfig as s, CANONICAL_FIELDS as t, projectionFields as u, columnMetaFor as v, primaryKeyColumns as x, coversUniqueKey as y };
|
|
@@ -2137,6 +2137,23 @@ declare const ZodReadonly: $constructor<ZodReadonly>;
|
|
|
2137
2137
|
*/
|
|
2138
2138
|
/** The reserved ref name for the projection's base table. */
|
|
2139
2139
|
declare const BASE_REF = "base";
|
|
2140
|
+
/**
|
|
2141
|
+
* Entity keys become view names (`crm_src_<entityKey>`), so keep them slugs.
|
|
2142
|
+
*
|
|
2143
|
+
* Accepts hyphens as well as underscores, matching `OBJECT_TYPE_SLUG_PATTERN`
|
|
2144
|
+
* on the server. Those two were widened apart: the server learned hyphens
|
|
2145
|
+
* because the SDK's own `slugify` collapses non-alphanumerics to them and
|
|
2146
|
+
* `clivly status` reports that form as canonical — but this pattern was missed,
|
|
2147
|
+
* so `clivly status` would tell you to key an entity `client-accounts` and then
|
|
2148
|
+
* `defineClivlyConfig` would reject the config it had just asked for. Same bug,
|
|
2149
|
+
* opposite half of the product.
|
|
2150
|
+
*
|
|
2151
|
+
* Widening is the safe direction: every key that was valid before still is, so
|
|
2152
|
+
* nothing existing is renamed. Safe at all because neither use is ever a bare
|
|
2153
|
+
* SQL identifier — the view name is quoted (`view-compiler.ts`, `quoteIdent`),
|
|
2154
|
+
* and so is a join's `key` when it is emitted as an alias.
|
|
2155
|
+
*/
|
|
2156
|
+
declare const PROJECTION_KEY_PATTERN: RegExp;
|
|
2140
2157
|
declare const projectionRefSchema: ZodObject<{
|
|
2141
2158
|
table: ZodString;
|
|
2142
2159
|
column: ZodString;
|
|
@@ -2638,6 +2655,15 @@ declare function customObjectDeclarations(config: ClivlyEntitiesConfig): CustomO
|
|
|
2638
2655
|
* Qualified rather than bare: two bindings can pull the same column name from
|
|
2639
2656
|
* different tables (`cl.full_name` and `adv.full_name`), which a bare column
|
|
2640
2657
|
* name cannot tell apart.
|
|
2658
|
+
*
|
|
2659
|
+
* This is the single derivation of that map, and both consumers depend on it
|
|
2660
|
+
* emitting ref tokens. `mappingsToEntitiesConfig` populates a projection
|
|
2661
|
+
* entity's required `fields`; the cloud ingest path needs the same map because
|
|
2662
|
+
* a projection's persisted `crm_entity_mappings.field_map` is empty by design,
|
|
2663
|
+
* so `rowToSourceRow` derives the effective field map from the projection
|
|
2664
|
+
* instead. The tokens emitted here are exactly how `fromDrizzleProjection` keys
|
|
2665
|
+
* the rows it pushes (`drizzle-projection.ts` `refToken`), so the two line up.
|
|
2666
|
+
* Deriving this a second time anywhere would let the copies drift.
|
|
2641
2667
|
*/
|
|
2642
2668
|
declare function projectionFields(projection: ProjectionMapping): Record<string, string>;
|
|
2643
2669
|
declare function defineClivlyConfig(config: ClivlyEntitiesConfigInput, options?: {
|
|
@@ -2698,4 +2724,4 @@ interface ConfigValidationResult {
|
|
|
2698
2724
|
*/
|
|
2699
2725
|
declare function validateEntitiesConfig(config: ClivlyEntitiesConfig, schema: DiscoveredSchemaTable[]): ConfigValidationResult;
|
|
2700
2726
|
//#endregion
|
|
2701
|
-
export { BASE_REF as A, defineClivlyConfig as C, relationshipSchema as D, projectionFields as E,
|
|
2727
|
+
export { BASE_REF as A, defineClivlyConfig as C, relationshipSchema as D, projectionFields as E, ProjectionMapping as F, ProjectionRef as I, identityRefs as L, ProjectionFieldBinding as M, ProjectionIdentity as N, relationshipsSchema as O, ProjectionJoin as P, projectionSchema as R, customObjectDeclarations as S, filterSchema as T, MATERIALIZING_CONCEPTS as _, ClivlyEntitiesConfig as a, RelationshipVia as b, ClivlyEntityConfigInput as c, CrmConcept as d, CustomObjectDeclaration as f, FilterValue as g, FilterExpr as h, ClivlyConfigError as i, PROJECTION_KEY_PATTERN as j, validateEntitiesConfig as k, ConfigValidationError as l, DiscoveredSchemaTable as m, CRM_CONCEPTS as n, ClivlyEntitiesConfigInput as o, DiscoveredColumnMeta as p, CUSTOM_CONCEPT as r, ClivlyEntityConfig as s, CANONICAL_FIELDS as t, ConfigValidationResult as u, MaterializingConcept as v, entitiesConfigSchema as w, RelationshipsMap as x, RelationshipSpec as y };
|
|
@@ -2137,6 +2137,23 @@ declare const ZodReadonly: $constructor<ZodReadonly>;
|
|
|
2137
2137
|
*/
|
|
2138
2138
|
/** The reserved ref name for the projection's base table. */
|
|
2139
2139
|
declare const BASE_REF = "base";
|
|
2140
|
+
/**
|
|
2141
|
+
* Entity keys become view names (`crm_src_<entityKey>`), so keep them slugs.
|
|
2142
|
+
*
|
|
2143
|
+
* Accepts hyphens as well as underscores, matching `OBJECT_TYPE_SLUG_PATTERN`
|
|
2144
|
+
* on the server. Those two were widened apart: the server learned hyphens
|
|
2145
|
+
* because the SDK's own `slugify` collapses non-alphanumerics to them and
|
|
2146
|
+
* `clivly status` reports that form as canonical — but this pattern was missed,
|
|
2147
|
+
* so `clivly status` would tell you to key an entity `client-accounts` and then
|
|
2148
|
+
* `defineClivlyConfig` would reject the config it had just asked for. Same bug,
|
|
2149
|
+
* opposite half of the product.
|
|
2150
|
+
*
|
|
2151
|
+
* Widening is the safe direction: every key that was valid before still is, so
|
|
2152
|
+
* nothing existing is renamed. Safe at all because neither use is ever a bare
|
|
2153
|
+
* SQL identifier — the view name is quoted (`view-compiler.ts`, `quoteIdent`),
|
|
2154
|
+
* and so is a join's `key` when it is emitted as an alias.
|
|
2155
|
+
*/
|
|
2156
|
+
declare const PROJECTION_KEY_PATTERN: RegExp;
|
|
2140
2157
|
declare const projectionRefSchema: ZodObject<{
|
|
2141
2158
|
table: ZodString;
|
|
2142
2159
|
column: ZodString;
|
|
@@ -2638,6 +2655,15 @@ declare function customObjectDeclarations(config: ClivlyEntitiesConfig): CustomO
|
|
|
2638
2655
|
* Qualified rather than bare: two bindings can pull the same column name from
|
|
2639
2656
|
* different tables (`cl.full_name` and `adv.full_name`), which a bare column
|
|
2640
2657
|
* name cannot tell apart.
|
|
2658
|
+
*
|
|
2659
|
+
* This is the single derivation of that map, and both consumers depend on it
|
|
2660
|
+
* emitting ref tokens. `mappingsToEntitiesConfig` populates a projection
|
|
2661
|
+
* entity's required `fields`; the cloud ingest path needs the same map because
|
|
2662
|
+
* a projection's persisted `crm_entity_mappings.field_map` is empty by design,
|
|
2663
|
+
* so `rowToSourceRow` derives the effective field map from the projection
|
|
2664
|
+
* instead. The tokens emitted here are exactly how `fromDrizzleProjection` keys
|
|
2665
|
+
* the rows it pushes (`drizzle-projection.ts` `refToken`), so the two line up.
|
|
2666
|
+
* Deriving this a second time anywhere would let the copies drift.
|
|
2641
2667
|
*/
|
|
2642
2668
|
declare function projectionFields(projection: ProjectionMapping): Record<string, string>;
|
|
2643
2669
|
declare function defineClivlyConfig(config: ClivlyEntitiesConfigInput, options?: {
|
|
@@ -2698,4 +2724,4 @@ interface ConfigValidationResult {
|
|
|
2698
2724
|
*/
|
|
2699
2725
|
declare function validateEntitiesConfig(config: ClivlyEntitiesConfig, schema: DiscoveredSchemaTable[]): ConfigValidationResult;
|
|
2700
2726
|
//#endregion
|
|
2701
|
-
export { BASE_REF as A, defineClivlyConfig as C, relationshipSchema as D, projectionFields as E,
|
|
2727
|
+
export { BASE_REF as A, defineClivlyConfig as C, relationshipSchema as D, projectionFields as E, ProjectionMapping as F, ProjectionRef as I, identityRefs as L, ProjectionFieldBinding as M, ProjectionIdentity as N, relationshipsSchema as O, ProjectionJoin as P, projectionSchema as R, customObjectDeclarations as S, filterSchema as T, MATERIALIZING_CONCEPTS as _, ClivlyEntitiesConfig as a, RelationshipVia as b, ClivlyEntityConfigInput as c, CrmConcept as d, CustomObjectDeclaration as f, FilterValue as g, FilterExpr as h, ClivlyConfigError as i, PROJECTION_KEY_PATTERN as j, validateEntitiesConfig as k, ConfigValidationError as l, DiscoveredSchemaTable as m, CRM_CONCEPTS as n, ClivlyEntitiesConfigInput as o, DiscoveredColumnMeta as p, CUSTOM_CONCEPT as r, ClivlyEntityConfig as s, CANONICAL_FIELDS as t, ConfigValidationResult as u, MaterializingConcept as v, entitiesConfigSchema as w, RelationshipsMap as x, RelationshipSpec as y };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_projection = require("./projection-
|
|
1
|
+
const require_projection = require("./projection-Dz6_i1-w.cjs");
|
|
2
2
|
//#region ../core/src/schema-meta.ts
|
|
3
3
|
/**
|
|
4
4
|
* Accessors over a discovered table's schema-v2 metadata.
|
|
@@ -29,7 +29,17 @@ function coversUniqueKey(table, columns) {
|
|
|
29
29
|
const present = new Set(columns);
|
|
30
30
|
return (table.uniqueConstraints ?? []).some((key) => key.length > 0 && key.every((column) => present.has(column)));
|
|
31
31
|
}
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Is this table's payload rich enough to validate a projection against?
|
|
34
|
+
*
|
|
35
|
+
* A type predicate rather than a plain boolean so the narrowing is visible to
|
|
36
|
+
* the compiler: callers that guard on this can iterate `columnsMeta` directly
|
|
37
|
+
* instead of casting, and a cast that outlives its guard becomes a type error
|
|
38
|
+
* rather than a silent walk over `undefined`.
|
|
39
|
+
*
|
|
40
|
+
* The invariant is unchanged: this reports whether the metadata is *known*,
|
|
41
|
+
* never whether the table has keys. Absent metadata stays UNKNOWN.
|
|
42
|
+
*/
|
|
33
43
|
function hasRichMeta(table) {
|
|
34
44
|
return Boolean(table.columnsMeta && table.uniqueConstraints);
|
|
35
45
|
}
|
|
@@ -273,6 +283,64 @@ function checkFanOut(errors, projection, resolver, base, basePk) {
|
|
|
273
283
|
});
|
|
274
284
|
}
|
|
275
285
|
/**
|
|
286
|
+
* One error naming a filter column that is not on the projection's base table,
|
|
287
|
+
* and the base table it would have to be on.
|
|
288
|
+
*/
|
|
289
|
+
function columnOnBase(column, baseTable, path) {
|
|
290
|
+
if (baseTable?.columns.includes(column)) return [];
|
|
291
|
+
return [{
|
|
292
|
+
path,
|
|
293
|
+
message: `filter column "${column}" does not exist on "${baseTable?.name ?? "the base table"}" — a derived object is filtered by its base table's columns only, so name one of those, or move the condition into a join's \`on\``
|
|
294
|
+
}];
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* A derived object's filter is evaluated in the cloud against pushed rows, so
|
|
298
|
+
* it can only name columns of the base table.
|
|
299
|
+
*
|
|
300
|
+
* Every rejected shape here fails *silently* today, which is the whole reason
|
|
301
|
+
* the check exists — a filter that cannot be honoured must error, never quietly
|
|
302
|
+
* return the wrong rows. The three shapes fail in different directions:
|
|
303
|
+
*
|
|
304
|
+
* - `$raw` is opaque SQL meant for the embedded model. The cloud path cannot
|
|
305
|
+
* evaluate it and passes every row, so the filter is ignored and the object
|
|
306
|
+
* syncs UNFILTERED.
|
|
307
|
+
* - A joined-table column resolves to nothing on a pushed row, so under a
|
|
308
|
+
* positive operator (`eq`, `in`) no row matches and the object syncs EMPTY.
|
|
309
|
+
* - The same unresolvable column under a NEGATIVE operator (`ne`, `isNull`,
|
|
310
|
+
* `notIn`) is the dangerous one: the missing cell reads as null, so the
|
|
311
|
+
* predicate is true for every row and a filter meant to narrow the sync
|
|
312
|
+
* ADMITS EVERYTHING. Nobody notices extra rows arriving.
|
|
313
|
+
*
|
|
314
|
+
* Rejecting an unresolvable column closes the last two together.
|
|
315
|
+
*/
|
|
316
|
+
function validateProjectionFilter(filter, projection, schema, base) {
|
|
317
|
+
if (!filter) return [];
|
|
318
|
+
const path = `${base}.filter`;
|
|
319
|
+
if ("$raw" in filter) return [{
|
|
320
|
+
path,
|
|
321
|
+
message: "a $raw filter cannot be evaluated against a derived object's rows — it is opaque SQL, so the cloud passes every row through unfiltered. Express it as column conditions on the base table instead"
|
|
322
|
+
}];
|
|
323
|
+
const baseTable = schema.find((t) => t.name === projection.baseTable);
|
|
324
|
+
const errors = [];
|
|
325
|
+
for (const column of Object.keys(filter)) {
|
|
326
|
+
if (column.includes(".")) {
|
|
327
|
+
const [ref, ...rest] = column.split(".");
|
|
328
|
+
const bare = rest.join(".");
|
|
329
|
+
if (ref !== "base") {
|
|
330
|
+
errors.push({
|
|
331
|
+
path,
|
|
332
|
+
message: `filter column "${column}" names the join "${ref}", but a derived object is filtered by its base table ("${projection.baseTable}") only — filter on a base column, or move the condition into that join's \`on\``
|
|
333
|
+
});
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
errors.push(...columnOnBase(bare, baseTable, path));
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
errors.push(...columnOnBase(column, baseTable, path));
|
|
340
|
+
}
|
|
341
|
+
return errors;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
276
344
|
* Validate a projection against the discovered schema. Returns every error
|
|
277
345
|
* found (not just the first) so a builder UI can show them all at once.
|
|
278
346
|
*/
|
|
@@ -545,6 +613,15 @@ function customObjectDeclarations(config) {
|
|
|
545
613
|
* Qualified rather than bare: two bindings can pull the same column name from
|
|
546
614
|
* different tables (`cl.full_name` and `adv.full_name`), which a bare column
|
|
547
615
|
* name cannot tell apart.
|
|
616
|
+
*
|
|
617
|
+
* This is the single derivation of that map, and both consumers depend on it
|
|
618
|
+
* emitting ref tokens. `mappingsToEntitiesConfig` populates a projection
|
|
619
|
+
* entity's required `fields`; the cloud ingest path needs the same map because
|
|
620
|
+
* a projection's persisted `crm_entity_mappings.field_map` is empty by design,
|
|
621
|
+
* so `rowToSourceRow` derives the effective field map from the projection
|
|
622
|
+
* instead. The tokens emitted here are exactly how `fromDrizzleProjection` keys
|
|
623
|
+
* the rows it pushes (`drizzle-projection.ts` `refToken`), so the two line up.
|
|
624
|
+
* Deriving this a second time anywhere would let the copies drift.
|
|
548
625
|
*/
|
|
549
626
|
function projectionFields(projection) {
|
|
550
627
|
const fields = {};
|
|
@@ -683,7 +760,7 @@ function validateEntity(v, config, key, entity, schema) {
|
|
|
683
760
|
if (entity.source !== entity.projection.baseTable) v.report(`${base}.source`, `"source" ("${entity.source}") must match "projection.baseTable" ("${entity.projection.baseTable}") — the view compiler reads baseTable and the sync engine reads source, so a mismatch makes them disagree about which table this entity is`);
|
|
684
761
|
for (const error of validateProjection(entity.projection, schema, base)) v.report(error.path, error.message);
|
|
685
762
|
validateReadOnly(v, base, entity);
|
|
686
|
-
|
|
763
|
+
for (const error of validateProjectionFilter(entity.filter, entity.projection, schema, base)) v.report(error.path, error.message);
|
|
687
764
|
return;
|
|
688
765
|
}
|
|
689
766
|
validateFields(v, base, entity);
|
|
@@ -829,3 +906,9 @@ Object.defineProperty(exports, "validateProjection", {
|
|
|
829
906
|
return validateProjection;
|
|
830
907
|
}
|
|
831
908
|
});
|
|
909
|
+
Object.defineProperty(exports, "validateProjectionFilter", {
|
|
910
|
+
enumerable: true,
|
|
911
|
+
get: function() {
|
|
912
|
+
return validateProjectionFilter;
|
|
913
|
+
}
|
|
914
|
+
});
|
package/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const require_entity_config = require("./entity-config-
|
|
3
|
+
const require_entity_config = require("./entity-config-DgAe0_YD.cjs");
|
|
4
4
|
require("./mapping-score-CaB3F4Uu.cjs");
|
|
5
5
|
const require_internal_entity_heuristics = require("./internal-entity-heuristics.cjs");
|
|
6
|
-
const require_sdk = require("./sdk-
|
|
6
|
+
const require_sdk = require("./sdk-B1vX4kUJ.cjs");
|
|
7
7
|
let node_child_process = require("node:child_process");
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
let node_module = require("node:module");
|
|
@@ -306,16 +306,27 @@ function addCommand(pm, packages) {
|
|
|
306
306
|
*/
|
|
307
307
|
const CLIVLY_TICK_PATH = "/api/clivly/tick";
|
|
308
308
|
/**
|
|
309
|
-
*
|
|
309
|
+
* The example shape `clivly init`/`clivly login` show for `CLIVLY_SYNC_TRIGGER_URL`
|
|
310
|
+
* — commented out in `.env`, not written as a live value.
|
|
310
311
|
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
* `
|
|
312
|
+
* It used to be scaffolded uncommented, on the theory that a visible template
|
|
313
|
+
* beats an empty line. The SDK's `isPlaceholderUrl` guard
|
|
314
|
+
* (`@clivly/sdk/trigger-url`) already keeps a bracketed placeholder like this
|
|
315
|
+
* one out of `syncTrigger.url` on every heartbeat — `resolveTriggerUrl` skips
|
|
316
|
+
* it in favour of the dev tunnel / platform-derived origin — and
|
|
317
|
+
* `assertSafeTriggerUrl` on the API side rejects it outright if it were ever
|
|
318
|
+
* sent, since `<yourDomain.com>` doesn't even parse as a URL. So it was never
|
|
319
|
+
* actually fireable (F-12 is not an SSRF/overwrite bug).
|
|
314
320
|
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
321
|
+
* What it DID break: readiness reporting only checked *presence*, not
|
|
322
|
+
* validity, so an unedited placeholder made the dashboard's setup ladder and
|
|
323
|
+
* `triggerConfigured` report "done" while `clivly status` correctly flagged
|
|
324
|
+
* it — two surfaces disagreeing about a workspace that could never run "Run
|
|
325
|
+
* sync". See `hasWorkingTrigger` (`@clivly.com/api/sync-trigger`) for the
|
|
326
|
+
* fix. Commenting the example out here removes the confusing truthy-but-fake
|
|
327
|
+
* value at the source, on top of that fix, while keeping the shape visible
|
|
328
|
+
* for the (uncommon) case a developer needs it — most platforms are
|
|
329
|
+
* auto-detected, and `clivly dev` handles local development.
|
|
319
330
|
*/
|
|
320
331
|
const TRIGGER_URL_PLACEHOLDER = `https://<yourDomain.com>${CLIVLY_TICK_PATH}`;
|
|
321
332
|
//#endregion
|
|
@@ -1440,8 +1451,8 @@ function credentialEntries(credentials) {
|
|
|
1440
1451
|
},
|
|
1441
1452
|
{
|
|
1442
1453
|
key: "CLIVLY_SYNC_TRIGGER_URL",
|
|
1443
|
-
value:
|
|
1444
|
-
comment:
|
|
1454
|
+
value: "",
|
|
1455
|
+
comment: `Public URL of your tick route. Only needed when the platform's own URL can't be auto-detected (see \`clivly status\`). Uncomment and set your deployed origin if so:\n# CLIVLY_SYNC_TRIGGER_URL=${TRIGGER_URL_PLACEHOLDER}`
|
|
1445
1456
|
}
|
|
1446
1457
|
];
|
|
1447
1458
|
}
|
|
@@ -1810,8 +1821,7 @@ const CLIVLY_ENV_KEYS = [
|
|
|
1810
1821
|
},
|
|
1811
1822
|
{
|
|
1812
1823
|
key: "CLIVLY_SYNC_TRIGGER_URL",
|
|
1813
|
-
|
|
1814
|
-
comment: "Public URL of your tick route — replace <yourDomain.com> once deployed. Local dev: run `clivly dev` instead."
|
|
1824
|
+
comment: `Public URL of your tick route. Only needed when the platform's own URL can't be auto-detected (see \`clivly status\`). Uncomment and set your deployed origin if so:\n# CLIVLY_SYNC_TRIGGER_URL=${TRIGGER_URL_PLACEHOLDER}`
|
|
1815
1825
|
},
|
|
1816
1826
|
{
|
|
1817
1827
|
key: "CLIVLY_SYNC_TRIGGER_SECRET",
|
package/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as CANONICAL_FIELDS } from "./entity-config-
|
|
2
|
+
import { t as CANONICAL_FIELDS } from "./entity-config-C4d3K9CP.mjs";
|
|
3
3
|
import "./mapping-score-C68uAqGB.mjs";
|
|
4
4
|
import { matchesConcept, requiredFieldsFor, scoreFieldMap, scoreRelationships, suggestCursorField, toFieldMap } from "./internal-entity-heuristics.mjs";
|
|
5
|
-
import { s as isPlaceholderUrl, t as createClivlySDK } from "./sdk-
|
|
5
|
+
import { s as isPlaceholderUrl, t as createClivlySDK } from "./sdk-D6gX0TXS.mjs";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
8
|
import { accessSync, constants, existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, statSync, watch, writeFileSync } from "node:fs";
|
|
@@ -305,16 +305,27 @@ function addCommand(pm, packages) {
|
|
|
305
305
|
*/
|
|
306
306
|
const CLIVLY_TICK_PATH = "/api/clivly/tick";
|
|
307
307
|
/**
|
|
308
|
-
*
|
|
308
|
+
* The example shape `clivly init`/`clivly login` show for `CLIVLY_SYNC_TRIGGER_URL`
|
|
309
|
+
* — commented out in `.env`, not written as a live value.
|
|
309
310
|
*
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
* `
|
|
311
|
+
* It used to be scaffolded uncommented, on the theory that a visible template
|
|
312
|
+
* beats an empty line. The SDK's `isPlaceholderUrl` guard
|
|
313
|
+
* (`@clivly/sdk/trigger-url`) already keeps a bracketed placeholder like this
|
|
314
|
+
* one out of `syncTrigger.url` on every heartbeat — `resolveTriggerUrl` skips
|
|
315
|
+
* it in favour of the dev tunnel / platform-derived origin — and
|
|
316
|
+
* `assertSafeTriggerUrl` on the API side rejects it outright if it were ever
|
|
317
|
+
* sent, since `<yourDomain.com>` doesn't even parse as a URL. So it was never
|
|
318
|
+
* actually fireable (F-12 is not an SSRF/overwrite bug).
|
|
313
319
|
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
320
|
+
* What it DID break: readiness reporting only checked *presence*, not
|
|
321
|
+
* validity, so an unedited placeholder made the dashboard's setup ladder and
|
|
322
|
+
* `triggerConfigured` report "done" while `clivly status` correctly flagged
|
|
323
|
+
* it — two surfaces disagreeing about a workspace that could never run "Run
|
|
324
|
+
* sync". See `hasWorkingTrigger` (`@clivly.com/api/sync-trigger`) for the
|
|
325
|
+
* fix. Commenting the example out here removes the confusing truthy-but-fake
|
|
326
|
+
* value at the source, on top of that fix, while keeping the shape visible
|
|
327
|
+
* for the (uncommon) case a developer needs it — most platforms are
|
|
328
|
+
* auto-detected, and `clivly dev` handles local development.
|
|
318
329
|
*/
|
|
319
330
|
const TRIGGER_URL_PLACEHOLDER = `https://<yourDomain.com>${CLIVLY_TICK_PATH}`;
|
|
320
331
|
//#endregion
|
|
@@ -1439,8 +1450,8 @@ function credentialEntries(credentials) {
|
|
|
1439
1450
|
},
|
|
1440
1451
|
{
|
|
1441
1452
|
key: "CLIVLY_SYNC_TRIGGER_URL",
|
|
1442
|
-
value:
|
|
1443
|
-
comment:
|
|
1453
|
+
value: "",
|
|
1454
|
+
comment: `Public URL of your tick route. Only needed when the platform's own URL can't be auto-detected (see \`clivly status\`). Uncomment and set your deployed origin if so:\n# CLIVLY_SYNC_TRIGGER_URL=${TRIGGER_URL_PLACEHOLDER}`
|
|
1444
1455
|
}
|
|
1445
1456
|
];
|
|
1446
1457
|
}
|
|
@@ -1809,8 +1820,7 @@ const CLIVLY_ENV_KEYS = [
|
|
|
1809
1820
|
},
|
|
1810
1821
|
{
|
|
1811
1822
|
key: "CLIVLY_SYNC_TRIGGER_URL",
|
|
1812
|
-
|
|
1813
|
-
comment: "Public URL of your tick route — replace <yourDomain.com> once deployed. Local dev: run `clivly dev` instead."
|
|
1823
|
+
comment: `Public URL of your tick route. Only needed when the platform's own URL can't be auto-detected (see \`clivly status\`). Uncomment and set your deployed origin if so:\n# CLIVLY_SYNC_TRIGGER_URL=${TRIGGER_URL_PLACEHOLDER}`
|
|
1814
1824
|
},
|
|
1815
1825
|
{
|
|
1816
1826
|
key: "CLIVLY_SYNC_TRIGGER_SECRET",
|