clivly 0.9.2 → 0.9.4
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 +60 -6
- package/core-entity-config.cjs +1 -1
- package/core-entity-config.mjs +1 -1
- package/core-mappings-config.cjs +1 -1
- package/core-mappings-config.mjs +1 -1
- package/core-sync-engine.cjs +1 -1
- package/core-sync-engine.mjs +1 -1
- package/core-view-compiler.cjs +1 -1
- package/core-view-compiler.mjs +1 -1
- package/core.cjs +11 -6
- package/core.d.cts +3 -2
- package/core.d.mts +3 -2
- package/core.mjs +8 -7
- package/drizzle.cjs +74 -9
- package/drizzle.d.cts +83 -7
- package/drizzle.d.mts +83 -7
- package/drizzle.mjs +74 -10
- package/{entity-config-DtBsl2MQ.cjs → entity-config-7sGCk_nw.cjs} +1 -1
- package/{entity-config-kUHri9yA.mjs → entity-config-B74SPOUZ.mjs} +1 -1
- package/index.cjs +141 -29
- package/index.d.cts +2 -1
- package/index.d.mts +2 -1
- package/index.mjs +141 -30
- package/integration-state-BK-riZnm.d.cts +130 -0
- package/integration-state-BK-riZnm.d.mts +130 -0
- package/{internal-Df-PtOH_.mjs → internal-CykXpEGk.mjs} +2 -2
- package/{internal-CwnEG6Wl.cjs → internal-DqduZU3l.cjs} +2 -2
- package/internal-entity-config.cjs +1 -1
- package/internal-entity-config.mjs +1 -1
- package/internal-entity-heuristics.cjs +1 -1
- package/internal-entity-heuristics.mjs +1 -1
- package/internal-mappings-config.cjs +1 -1
- package/internal-mappings-config.mjs +1 -1
- package/{internal-CDY8g9Ic.d.mts → internal-o2M7FGlR.d.mts} +29 -1
- package/internal-sync-engine.cjs +1 -1
- package/internal-sync-engine.mjs +1 -1
- package/internal-view-compiler.cjs +1 -1
- package/internal-view-compiler.mjs +1 -1
- package/{internal-Da_dlcQ_.d.cts → internal-wCXSELDN.d.cts} +29 -1
- package/internal.cjs +11 -6
- package/internal.d.cts +3 -2
- package/internal.d.mts +3 -2
- package/internal.mjs +8 -7
- package/{mappings-config-B0RqFLxu.cjs → mappings-config-CLyEg0sf.cjs} +1 -1
- package/{mappings-config-B1b1HVBg.mjs → mappings-config-DQS_s4dg.mjs} +1 -1
- package/{namespace-probe-C6aHjedO.d.cts → namespace-probe-BoGI18dQ.d.cts} +15 -0
- package/{namespace-probe-Dj-0Q3Hb.d.mts → namespace-probe-Cn7voAd0.d.mts} +15 -0
- package/package.json +1 -1
- package/{projection-D7xmQP4S.mjs → projection-CSGJbZlZ.mjs} +25 -4
- package/{projection-C5244vGa.cjs → projection-Dz6_i1-w.cjs} +25 -4
- package/projection-sql-BgnfiGqF.mjs +65 -0
- package/projection-sql-Cl1qIn8m.cjs +94 -0
- package/{sdk-D4Wp_uxb.cjs → sdk-CM1UO-I7.cjs} +59 -15
- package/{sdk-DXV8M_Io.mjs → sdk-DYBDR939.mjs} +54 -16
- package/sdk.cjs +3 -2
- package/sdk.d.cts +5 -2
- package/sdk.d.mts +5 -2
- package/sdk.mjs +3 -3
- package/{sync-engine-abE94g6X.cjs → sync-engine-C4A5jgA4.cjs} +1 -1
- package/{sync-engine-SuxSzbZ4.mjs → sync-engine-Dt5QQSKO.mjs} +1 -1
- package/{view-compiler-Blu4OXJ7.cjs → view-compiler-B5nDal14.cjs} +21 -74
- package/{view-compiler-CQx8TRrW.mjs → view-compiler-CSYwEWf5.mjs} +2 -55
- package/vite.d.cts +1 -1
- package/vite.d.mts +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const require_entity_config = require("./entity-config-
|
|
2
|
-
const require_mappings_config = require("./mappings-config-
|
|
1
|
+
const require_entity_config = require("./entity-config-7sGCk_nw.cjs");
|
|
2
|
+
const require_mappings_config = require("./mappings-config-CLyEg0sf.cjs");
|
|
3
3
|
const require_drizzle_meta = require("./drizzle-meta-Bb1JWYqf.cjs");
|
|
4
4
|
//#region ../sdk/src/deployment-identity.ts
|
|
5
5
|
const MAX_DEPLOYMENT_ID_LENGTH = 128;
|
|
@@ -123,11 +123,16 @@ function checkConceptCoverage(config) {
|
|
|
123
123
|
}
|
|
124
124
|
//#endregion
|
|
125
125
|
//#region ../sdk/src/custom-slug.ts
|
|
126
|
+
const SLUG_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
126
127
|
const NON_ALNUM = /[^a-z0-9]+/g;
|
|
127
128
|
const EDGE_HYPHENS = /(^-|-$)/g;
|
|
128
129
|
function slugify(value) {
|
|
129
130
|
return value.toLowerCase().trim().replace(NON_ALNUM, "-").replace(EDGE_HYPHENS, "");
|
|
130
131
|
}
|
|
132
|
+
function suggestionFor(declared) {
|
|
133
|
+
const candidate = slugify(declared);
|
|
134
|
+
return candidate !== declared && SLUG_PATTERN.test(candidate) ? ` Did you mean "${candidate}"?` : "";
|
|
135
|
+
}
|
|
131
136
|
function checkCustomObjectSlugs(config) {
|
|
132
137
|
const custom = config.source?.custom ?? [];
|
|
133
138
|
const declared = config.entities ? require_entity_config.customObjectDeclarations(config.entities) : [];
|
|
@@ -141,12 +146,11 @@ function checkCustomObjectSlugs(config) {
|
|
|
141
146
|
const seen = /* @__PURE__ */ new Map();
|
|
142
147
|
for (const source of custom) {
|
|
143
148
|
const declared = source.objectType;
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
else seen.set(canonical, source.entity);
|
|
149
|
+
if (declared === "") problems.push(`"${source.entity}" has an empty objectType.`);
|
|
150
|
+
else if (!SLUG_PATTERN.test(declared)) problems.push(`"${declared}" is not a valid object type slug — use lowercase letters, digits, "-" or "_", starting with a letter.${suggestionFor(declared)}`);
|
|
151
|
+
const prior = seen.get(declared);
|
|
152
|
+
if (prior && declared !== "") problems.push(`duplicate slug "${declared}" declared by "${prior}" and "${source.entity}".`);
|
|
153
|
+
else seen.set(declared, source.entity);
|
|
150
154
|
}
|
|
151
155
|
if (problems.length > 0) return {
|
|
152
156
|
name: "Custom object slugs",
|
|
@@ -253,10 +257,20 @@ async function runMappingDryRun(config, entityKey) {
|
|
|
253
257
|
detail: `"${resolved.source.entity}" is empty — nothing to map yet.`
|
|
254
258
|
};
|
|
255
259
|
const missing = Object.entries(fields).filter(([, column]) => !(column in row)).map(([field, column]) => `${field} → ${column}`);
|
|
260
|
+
if (missing.length === 0) return {
|
|
261
|
+
name,
|
|
262
|
+
ok: true,
|
|
263
|
+
detail: `Mapped ${Object.keys(fields).length} field(s) from a real "${resolved.source.entity}" row.`
|
|
264
|
+
};
|
|
265
|
+
if (config.entities?.entities?.[entityKey]?.projection !== void 0 && missing.length === Object.keys(fields).length) return {
|
|
266
|
+
name,
|
|
267
|
+
ok: false,
|
|
268
|
+
detail: `Row from "${resolved.source.entity}" carries none of the projection's columns (${missing.join(", ")}) — the source performs no joins, so the projection is declared but never executed. Build it with \`fromDrizzleProjection\` from \`clivly/drizzle\`, which runs the joins and returns rows keyed by ref token.`
|
|
269
|
+
};
|
|
256
270
|
return {
|
|
257
271
|
name,
|
|
258
|
-
ok:
|
|
259
|
-
detail:
|
|
272
|
+
ok: false,
|
|
273
|
+
detail: `Row from "${resolved.source.entity}" is missing mapped column(s): ${missing.join(", ")}.`
|
|
260
274
|
};
|
|
261
275
|
}
|
|
262
276
|
//#endregion
|
|
@@ -465,9 +479,27 @@ async function mappingDryRunChecks(config) {
|
|
|
465
479
|
return checks;
|
|
466
480
|
}
|
|
467
481
|
/**
|
|
482
|
+
* Why a source has no `DRIZZLE_META`, without asserting more than we know.
|
|
483
|
+
*
|
|
484
|
+
* There are two ways to get here and they are indistinguishable at runtime: the
|
|
485
|
+
* source really was hand-written, or it came from `fromDrizzle` and a spread
|
|
486
|
+
* stripped the metadata, which is non-enumerable. The second is easy to fall
|
|
487
|
+
* into precisely where `objectType` is present — `source.custom` is typed
|
|
488
|
+
* `Array<SyncSource & { objectType: string }>`, so attaching that property with
|
|
489
|
+
* `{ ...fromDrizzle(...), objectType }` used to be the only way to satisfy it.
|
|
490
|
+
*
|
|
491
|
+
* Flatly calling such a source "hand-written" sent developers looking for one
|
|
492
|
+
* that does not exist. Where `objectType` is present, name the likelier cause
|
|
493
|
+
* and the fix instead of guessing wrong with confidence.
|
|
494
|
+
*/
|
|
495
|
+
function missingMetaDetail(source) {
|
|
496
|
+
const skipped = "column check skipped";
|
|
497
|
+
if (source.objectType === void 0) return `"${source.entity}" is a hand-written source — ${skipped}.`;
|
|
498
|
+
return `"${source.entity}" has no drizzle metadata — ${skipped}. If it came from \`fromDrizzle\`, a spread such as \`{ ...fromDrizzle(...), objectType }\` dropped it; pass \`objectType\` to \`fromDrizzle\` instead.`;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
468
501
|
* Confirm each drizzle source actually has the cursor and id columns the sync
|
|
469
|
-
* pages on.
|
|
470
|
-
* rather than failed.
|
|
502
|
+
* pages on. Sources with no column metadata are skipped rather than failed.
|
|
471
503
|
*/
|
|
472
504
|
function cursorColumnChecks(sources) {
|
|
473
505
|
const checks = [];
|
|
@@ -478,7 +510,7 @@ function cursorColumnChecks(sources) {
|
|
|
478
510
|
name: `Cursor/id columns exist (${source.entity})`,
|
|
479
511
|
ok: false,
|
|
480
512
|
skipped: true,
|
|
481
|
-
detail:
|
|
513
|
+
detail: missingMetaDetail(source)
|
|
482
514
|
});
|
|
483
515
|
continue;
|
|
484
516
|
}
|
|
@@ -1117,11 +1149,16 @@ function observedTriggerUrl(req) {
|
|
|
1117
1149
|
return new URL(`${proto}://${host}${requestUrl.pathname}`).toString();
|
|
1118
1150
|
} catch {}
|
|
1119
1151
|
}
|
|
1152
|
+
const PLACEHOLDER_BRACKETS = /[<>]/;
|
|
1153
|
+
function isPlaceholderUrl(value) {
|
|
1154
|
+
return PLACEHOLDER_BRACKETS.test(value);
|
|
1155
|
+
}
|
|
1120
1156
|
/**
|
|
1121
1157
|
* Decide which trigger URL to report, in precedence order:
|
|
1122
1158
|
*
|
|
1123
1159
|
* 1. `configuredUrl` — an explicit `syncTrigger.url`. Someone typed this on
|
|
1124
|
-
* purpose, so it beats anything inferred.
|
|
1160
|
+
* purpose, so it beats anything inferred. An unedited placeholder is not
|
|
1161
|
+
* "on purpose" and is skipped.
|
|
1125
1162
|
* 2. platform env origin + `configuredPath` — available at boot, before any
|
|
1126
1163
|
* request has arrived. Needs the path because platform variables only give
|
|
1127
1164
|
* an origin.
|
|
@@ -1131,7 +1168,7 @@ function observedTriggerUrl(req) {
|
|
|
1131
1168
|
* doesn't need to know about it.
|
|
1132
1169
|
*/
|
|
1133
1170
|
function resolveTriggerUrl(args) {
|
|
1134
|
-
if (args.configuredUrl) return args.configuredUrl;
|
|
1171
|
+
if (args.configuredUrl && !isPlaceholderUrl(args.configuredUrl)) return args.configuredUrl;
|
|
1135
1172
|
const origin = originFromPlatformEnv(args.env);
|
|
1136
1173
|
if (origin && args.configuredPath) return `${origin}${args.configuredPath.startsWith("/") ? args.configuredPath : `/${args.configuredPath}`}`;
|
|
1137
1174
|
return args.observedUrl;
|
|
@@ -1789,6 +1826,7 @@ function createClivlySDK(config) {
|
|
|
1789
1826
|
reason: "ok",
|
|
1790
1827
|
retryable: false,
|
|
1791
1828
|
org: body?.org,
|
|
1829
|
+
workspace: body?.workspace ?? null,
|
|
1792
1830
|
..."appLastSeenAt" in body ? { appLastSeenAt: body.appLastSeenAt ?? null } : {}
|
|
1793
1831
|
};
|
|
1794
1832
|
}
|
|
@@ -1996,3 +2034,9 @@ Object.defineProperty(exports, "createClivlySDK", {
|
|
|
1996
2034
|
return createClivlySDK;
|
|
1997
2035
|
}
|
|
1998
2036
|
});
|
|
2037
|
+
Object.defineProperty(exports, "isPlaceholderUrl", {
|
|
2038
|
+
enumerable: true,
|
|
2039
|
+
get: function() {
|
|
2040
|
+
return isPlaceholderUrl;
|
|
2041
|
+
}
|
|
2042
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { o as customObjectDeclarations } from "./entity-config-
|
|
2
|
-
import { t as entitiesToMappings } from "./mappings-config-
|
|
1
|
+
import { o as customObjectDeclarations } from "./entity-config-B74SPOUZ.mjs";
|
|
2
|
+
import { t as entitiesToMappings } from "./mappings-config-DQS_s4dg.mjs";
|
|
3
3
|
import { t as DRIZZLE_META } from "./drizzle-meta-D-5k3n0i.mjs";
|
|
4
4
|
//#region ../sdk/src/deployment-identity.ts
|
|
5
5
|
const MAX_DEPLOYMENT_ID_LENGTH = 128;
|
|
@@ -123,11 +123,16 @@ function checkConceptCoverage(config) {
|
|
|
123
123
|
}
|
|
124
124
|
//#endregion
|
|
125
125
|
//#region ../sdk/src/custom-slug.ts
|
|
126
|
+
const SLUG_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
126
127
|
const NON_ALNUM = /[^a-z0-9]+/g;
|
|
127
128
|
const EDGE_HYPHENS = /(^-|-$)/g;
|
|
128
129
|
function slugify(value) {
|
|
129
130
|
return value.toLowerCase().trim().replace(NON_ALNUM, "-").replace(EDGE_HYPHENS, "");
|
|
130
131
|
}
|
|
132
|
+
function suggestionFor(declared) {
|
|
133
|
+
const candidate = slugify(declared);
|
|
134
|
+
return candidate !== declared && SLUG_PATTERN.test(candidate) ? ` Did you mean "${candidate}"?` : "";
|
|
135
|
+
}
|
|
131
136
|
function checkCustomObjectSlugs(config) {
|
|
132
137
|
const custom = config.source?.custom ?? [];
|
|
133
138
|
const declared = config.entities ? customObjectDeclarations(config.entities) : [];
|
|
@@ -141,12 +146,11 @@ function checkCustomObjectSlugs(config) {
|
|
|
141
146
|
const seen = /* @__PURE__ */ new Map();
|
|
142
147
|
for (const source of custom) {
|
|
143
148
|
const declared = source.objectType;
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
else seen.set(canonical, source.entity);
|
|
149
|
+
if (declared === "") problems.push(`"${source.entity}" has an empty objectType.`);
|
|
150
|
+
else if (!SLUG_PATTERN.test(declared)) problems.push(`"${declared}" is not a valid object type slug — use lowercase letters, digits, "-" or "_", starting with a letter.${suggestionFor(declared)}`);
|
|
151
|
+
const prior = seen.get(declared);
|
|
152
|
+
if (prior && declared !== "") problems.push(`duplicate slug "${declared}" declared by "${prior}" and "${source.entity}".`);
|
|
153
|
+
else seen.set(declared, source.entity);
|
|
150
154
|
}
|
|
151
155
|
if (problems.length > 0) return {
|
|
152
156
|
name: "Custom object slugs",
|
|
@@ -253,10 +257,20 @@ async function runMappingDryRun(config, entityKey) {
|
|
|
253
257
|
detail: `"${resolved.source.entity}" is empty — nothing to map yet.`
|
|
254
258
|
};
|
|
255
259
|
const missing = Object.entries(fields).filter(([, column]) => !(column in row)).map(([field, column]) => `${field} → ${column}`);
|
|
260
|
+
if (missing.length === 0) return {
|
|
261
|
+
name,
|
|
262
|
+
ok: true,
|
|
263
|
+
detail: `Mapped ${Object.keys(fields).length} field(s) from a real "${resolved.source.entity}" row.`
|
|
264
|
+
};
|
|
265
|
+
if (config.entities?.entities?.[entityKey]?.projection !== void 0 && missing.length === Object.keys(fields).length) return {
|
|
266
|
+
name,
|
|
267
|
+
ok: false,
|
|
268
|
+
detail: `Row from "${resolved.source.entity}" carries none of the projection's columns (${missing.join(", ")}) — the source performs no joins, so the projection is declared but never executed. Build it with \`fromDrizzleProjection\` from \`clivly/drizzle\`, which runs the joins and returns rows keyed by ref token.`
|
|
269
|
+
};
|
|
256
270
|
return {
|
|
257
271
|
name,
|
|
258
|
-
ok:
|
|
259
|
-
detail:
|
|
272
|
+
ok: false,
|
|
273
|
+
detail: `Row from "${resolved.source.entity}" is missing mapped column(s): ${missing.join(", ")}.`
|
|
260
274
|
};
|
|
261
275
|
}
|
|
262
276
|
//#endregion
|
|
@@ -465,9 +479,27 @@ async function mappingDryRunChecks(config) {
|
|
|
465
479
|
return checks;
|
|
466
480
|
}
|
|
467
481
|
/**
|
|
482
|
+
* Why a source has no `DRIZZLE_META`, without asserting more than we know.
|
|
483
|
+
*
|
|
484
|
+
* There are two ways to get here and they are indistinguishable at runtime: the
|
|
485
|
+
* source really was hand-written, or it came from `fromDrizzle` and a spread
|
|
486
|
+
* stripped the metadata, which is non-enumerable. The second is easy to fall
|
|
487
|
+
* into precisely where `objectType` is present — `source.custom` is typed
|
|
488
|
+
* `Array<SyncSource & { objectType: string }>`, so attaching that property with
|
|
489
|
+
* `{ ...fromDrizzle(...), objectType }` used to be the only way to satisfy it.
|
|
490
|
+
*
|
|
491
|
+
* Flatly calling such a source "hand-written" sent developers looking for one
|
|
492
|
+
* that does not exist. Where `objectType` is present, name the likelier cause
|
|
493
|
+
* and the fix instead of guessing wrong with confidence.
|
|
494
|
+
*/
|
|
495
|
+
function missingMetaDetail(source) {
|
|
496
|
+
const skipped = "column check skipped";
|
|
497
|
+
if (source.objectType === void 0) return `"${source.entity}" is a hand-written source — ${skipped}.`;
|
|
498
|
+
return `"${source.entity}" has no drizzle metadata — ${skipped}. If it came from \`fromDrizzle\`, a spread such as \`{ ...fromDrizzle(...), objectType }\` dropped it; pass \`objectType\` to \`fromDrizzle\` instead.`;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
468
501
|
* Confirm each drizzle source actually has the cursor and id columns the sync
|
|
469
|
-
* pages on.
|
|
470
|
-
* rather than failed.
|
|
502
|
+
* pages on. Sources with no column metadata are skipped rather than failed.
|
|
471
503
|
*/
|
|
472
504
|
function cursorColumnChecks(sources) {
|
|
473
505
|
const checks = [];
|
|
@@ -478,7 +510,7 @@ function cursorColumnChecks(sources) {
|
|
|
478
510
|
name: `Cursor/id columns exist (${source.entity})`,
|
|
479
511
|
ok: false,
|
|
480
512
|
skipped: true,
|
|
481
|
-
detail:
|
|
513
|
+
detail: missingMetaDetail(source)
|
|
482
514
|
});
|
|
483
515
|
continue;
|
|
484
516
|
}
|
|
@@ -1117,11 +1149,16 @@ function observedTriggerUrl(req) {
|
|
|
1117
1149
|
return new URL(`${proto}://${host}${requestUrl.pathname}`).toString();
|
|
1118
1150
|
} catch {}
|
|
1119
1151
|
}
|
|
1152
|
+
const PLACEHOLDER_BRACKETS = /[<>]/;
|
|
1153
|
+
function isPlaceholderUrl(value) {
|
|
1154
|
+
return PLACEHOLDER_BRACKETS.test(value);
|
|
1155
|
+
}
|
|
1120
1156
|
/**
|
|
1121
1157
|
* Decide which trigger URL to report, in precedence order:
|
|
1122
1158
|
*
|
|
1123
1159
|
* 1. `configuredUrl` — an explicit `syncTrigger.url`. Someone typed this on
|
|
1124
|
-
* purpose, so it beats anything inferred.
|
|
1160
|
+
* purpose, so it beats anything inferred. An unedited placeholder is not
|
|
1161
|
+
* "on purpose" and is skipped.
|
|
1125
1162
|
* 2. platform env origin + `configuredPath` — available at boot, before any
|
|
1126
1163
|
* request has arrived. Needs the path because platform variables only give
|
|
1127
1164
|
* an origin.
|
|
@@ -1131,7 +1168,7 @@ function observedTriggerUrl(req) {
|
|
|
1131
1168
|
* doesn't need to know about it.
|
|
1132
1169
|
*/
|
|
1133
1170
|
function resolveTriggerUrl(args) {
|
|
1134
|
-
if (args.configuredUrl) return args.configuredUrl;
|
|
1171
|
+
if (args.configuredUrl && !isPlaceholderUrl(args.configuredUrl)) return args.configuredUrl;
|
|
1135
1172
|
const origin = originFromPlatformEnv(args.env);
|
|
1136
1173
|
if (origin && args.configuredPath) return `${origin}${args.configuredPath.startsWith("/") ? args.configuredPath : `/${args.configuredPath}`}`;
|
|
1137
1174
|
return args.observedUrl;
|
|
@@ -1789,6 +1826,7 @@ function createClivlySDK(config) {
|
|
|
1789
1826
|
reason: "ok",
|
|
1790
1827
|
retryable: false,
|
|
1791
1828
|
org: body?.org,
|
|
1829
|
+
workspace: body?.workspace ?? null,
|
|
1792
1830
|
..."appLastSeenAt" in body ? { appLastSeenAt: body.appLastSeenAt ?? null } : {}
|
|
1793
1831
|
};
|
|
1794
1832
|
}
|
|
@@ -1960,4 +1998,4 @@ function createClivlySDK(config) {
|
|
|
1960
1998
|
};
|
|
1961
1999
|
}
|
|
1962
2000
|
//#endregion
|
|
1963
|
-
export { ClivlyRateLimitError as a, ClivlyNetworkError as i, ClivlyAuthError as n, createChatSessionHandler as o, ClivlyError as r, createClivlySDK as t };
|
|
2001
|
+
export { ClivlyRateLimitError as a, ClivlyNetworkError as i, ClivlyAuthError as n, createChatSessionHandler as o, ClivlyError as r, isPlaceholderUrl as s, createClivlySDK as t };
|
package/sdk.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_entity_config = require("./entity-config-
|
|
3
|
-
const require_sdk = require("./sdk-
|
|
2
|
+
const require_entity_config = require("./entity-config-7sGCk_nw.cjs");
|
|
3
|
+
const require_sdk = require("./sdk-CM1UO-I7.cjs");
|
|
4
4
|
exports.ClivlyAuthError = require_sdk.ClivlyAuthError;
|
|
5
5
|
exports.ClivlyError = require_sdk.ClivlyError;
|
|
6
6
|
exports.ClivlyNetworkError = require_sdk.ClivlyNetworkError;
|
|
@@ -9,4 +9,5 @@ exports.createChatSessionHandler = require_sdk.createChatSessionHandler;
|
|
|
9
9
|
exports.createClivlySDK = require_sdk.createClivlySDK;
|
|
10
10
|
exports.customObjectDeclarations = require_entity_config.customObjectDeclarations;
|
|
11
11
|
exports.defineClivlyConfig = require_entity_config.defineClivlyConfig;
|
|
12
|
+
exports.isPlaceholderUrl = require_sdk.isPlaceholderUrl;
|
|
12
13
|
exports.validateEntitiesConfig = require_entity_config.validateEntitiesConfig;
|
package/sdk.d.cts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { C as defineClivlyConfig, S as customObjectDeclarations, a as ClivlyEntitiesConfig, f as CustomObjectDeclaration, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, u as ConfigValidationResult } from "./entity-config-B2RA53nw.cjs";
|
|
2
|
-
import { a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore } from "./namespace-probe-
|
|
2
|
+
import { a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore } from "./namespace-probe-BoGI18dQ.cjs";
|
|
3
|
+
//#region ../sdk/src/trigger-url.d.ts
|
|
4
|
+
declare function isPlaceholderUrl(value: string): boolean;
|
|
5
|
+
//#endregion
|
|
3
6
|
//#region ../sdk/src/chat-session.d.ts
|
|
4
7
|
declare function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl, fetchImpl, resolveUser }: CreateChatSessionHandlerOptions): (request: Request) => Promise<Response>;
|
|
5
8
|
//#endregion
|
|
@@ -33,4 +36,4 @@ declare class ClivlyNetworkError extends ClivlyError {}
|
|
|
33
36
|
*/
|
|
34
37
|
declare function createClivlySDK(config: ClivlySDKConfig): ClivlySDK;
|
|
35
38
|
//#endregion
|
|
36
|
-
export { type ChatSessionRequestBody, type ChatWidgetVisitor, ClivlyAuthError, type ClivlyConnectReason, type ClivlyConnectResult, type ClivlyEntitiesConfig, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, type ClivlySDK, type ClivlySDKConfig, type ClivlyUser, type ConfigValidationError, type ConfigValidationResult, type CreateChatSessionHandlerOptions, type CursorStore, type CustomObjectDeclaration, type DiscoveredSchemaTable, type DiscoveredTable, type DoctorCheck, type DoctorReport, type EnsureSchemaResult, type EnsuredMappingResult, type EnsuredObjectTypeResult, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, validateEntitiesConfig };
|
|
39
|
+
export { type ChatSessionRequestBody, type ChatWidgetVisitor, ClivlyAuthError, type ClivlyConnectReason, type ClivlyConnectResult, type ClivlyEntitiesConfig, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, type ClivlySDK, type ClivlySDKConfig, type ClivlyUser, type ConfigValidationError, type ConfigValidationResult, type CreateChatSessionHandlerOptions, type CursorStore, type CustomObjectDeclaration, type DiscoveredSchemaTable, type DiscoveredTable, type DoctorCheck, type DoctorReport, type EnsureSchemaResult, type EnsuredMappingResult, type EnsuredObjectTypeResult, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, isPlaceholderUrl, validateEntitiesConfig };
|
package/sdk.d.mts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { C as defineClivlyConfig, S as customObjectDeclarations, a as ClivlyEntitiesConfig, f as CustomObjectDeclaration, k as validateEntitiesConfig, l as ConfigValidationError, m as DiscoveredSchemaTable, u as ConfigValidationResult } from "./entity-config-B2RA53nw.mjs";
|
|
2
|
-
import { a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore } from "./namespace-probe-
|
|
2
|
+
import { a as ClivlyConnectResult, c as ClivlyUser, d as DiscoveredTable, f as DoctorCheck, g as EnsuredObjectTypeResult, h as EnsuredMappingResult, i as ClivlyConnectReason, l as CreateChatSessionHandlerOptions, m as EnsureSchemaResult, n as ChatSessionRequestBody, o as ClivlySDK, p as DoctorReport, r as ChatWidgetVisitor, s as ClivlySDKConfig, u as CursorStore } from "./namespace-probe-Cn7voAd0.mjs";
|
|
3
|
+
//#region ../sdk/src/trigger-url.d.ts
|
|
4
|
+
declare function isPlaceholderUrl(value: string): boolean;
|
|
5
|
+
//#endregion
|
|
3
6
|
//#region ../sdk/src/chat-session.d.ts
|
|
4
7
|
declare function createChatSessionHandler({ allowedOrigins, apiKey, apiUrl, fetchImpl, resolveUser }: CreateChatSessionHandlerOptions): (request: Request) => Promise<Response>;
|
|
5
8
|
//#endregion
|
|
@@ -33,4 +36,4 @@ declare class ClivlyNetworkError extends ClivlyError {}
|
|
|
33
36
|
*/
|
|
34
37
|
declare function createClivlySDK(config: ClivlySDKConfig): ClivlySDK;
|
|
35
38
|
//#endregion
|
|
36
|
-
export { type ChatSessionRequestBody, type ChatWidgetVisitor, ClivlyAuthError, type ClivlyConnectReason, type ClivlyConnectResult, type ClivlyEntitiesConfig, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, type ClivlySDK, type ClivlySDKConfig, type ClivlyUser, type ConfigValidationError, type ConfigValidationResult, type CreateChatSessionHandlerOptions, type CursorStore, type CustomObjectDeclaration, type DiscoveredSchemaTable, type DiscoveredTable, type DoctorCheck, type DoctorReport, type EnsureSchemaResult, type EnsuredMappingResult, type EnsuredObjectTypeResult, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, validateEntitiesConfig };
|
|
39
|
+
export { type ChatSessionRequestBody, type ChatWidgetVisitor, ClivlyAuthError, type ClivlyConnectReason, type ClivlyConnectResult, type ClivlyEntitiesConfig, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, type ClivlySDK, type ClivlySDKConfig, type ClivlyUser, type ConfigValidationError, type ConfigValidationResult, type CreateChatSessionHandlerOptions, type CursorStore, type CustomObjectDeclaration, type DiscoveredSchemaTable, type DiscoveredTable, type DoctorCheck, type DoctorReport, type EnsureSchemaResult, type EnsuredMappingResult, type EnsuredObjectTypeResult, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, isPlaceholderUrl, validateEntitiesConfig };
|
package/sdk.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { o as customObjectDeclarations, p as validateEntitiesConfig, s as defineClivlyConfig } from "./entity-config-
|
|
2
|
-
import { a as ClivlyRateLimitError, i as ClivlyNetworkError, n as ClivlyAuthError, o as createChatSessionHandler, r as ClivlyError, t as createClivlySDK } from "./sdk-
|
|
3
|
-
export { ClivlyAuthError, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, validateEntitiesConfig };
|
|
1
|
+
import { o as customObjectDeclarations, p as validateEntitiesConfig, s as defineClivlyConfig } from "./entity-config-B74SPOUZ.mjs";
|
|
2
|
+
import { a as ClivlyRateLimitError, i as ClivlyNetworkError, n as ClivlyAuthError, o as createChatSessionHandler, r as ClivlyError, s as isPlaceholderUrl, t as createClivlySDK } from "./sdk-DYBDR939.mjs";
|
|
3
|
+
export { ClivlyAuthError, ClivlyError, ClivlyNetworkError, ClivlyRateLimitError, createChatSessionHandler, createClivlySDK, customObjectDeclarations, defineClivlyConfig, isPlaceholderUrl, validateEntitiesConfig };
|
|
@@ -1,58 +1,5 @@
|
|
|
1
|
-
const require_projection = require("./projection-
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SQL generation for projection-backed entities.
|
|
5
|
-
*
|
|
6
|
-
* Base and joins are aliased by their ref key (`base`, then each join's `key`),
|
|
7
|
-
* which is what allows the same table to be joined twice and keeps generated
|
|
8
|
-
* SQL independent of host table names.
|
|
9
|
-
*
|
|
10
|
-
* Pure string generation — no DB access. Identifiers are escaped so output is
|
|
11
|
-
* deterministic and injection-safe.
|
|
12
|
-
*/
|
|
13
|
-
/** Quote a single identifier part, escaping embedded double-quotes. */
|
|
14
|
-
function quoteIdent(name) {
|
|
15
|
-
return `"${name.replace(/"/g, "\"\"")}"`;
|
|
16
|
-
}
|
|
17
|
-
function refSql(ref) {
|
|
18
|
-
return `${quoteIdent(ref.table)}.${quoteIdent(ref.column)}`;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* The identity expression, aliased as `id`. Composite identities concatenate
|
|
22
|
-
* their parts with `::`; validation guarantees every part is NOT NULL and
|
|
23
|
-
* non-text, which is what makes the concatenation collision-free.
|
|
24
|
-
*/
|
|
25
|
-
function identitySql(projection, basePkColumn) {
|
|
26
|
-
const refs = require_projection.identityRefs(projection.identity, basePkColumn);
|
|
27
|
-
if (projection.identity.kind === "composite") return `CONCAT_WS('::', ${refs.map((ref) => `${refSql(ref)}::text`).join(", ")}) AS ${quoteIdent("id")}`;
|
|
28
|
-
const [ref] = refs;
|
|
29
|
-
if (!ref) throw new Error("projection identity resolved to no column");
|
|
30
|
-
return `${refSql(ref)} AS ${quoteIdent("id")}`;
|
|
31
|
-
}
|
|
32
|
-
function compileProjectionSelect(projection, basePkColumn) {
|
|
33
|
-
const selectItems = [identitySql(projection, basePkColumn)];
|
|
34
|
-
const columns = ["id"];
|
|
35
|
-
for (const [field, binding] of Object.entries(projection.fieldBindings)) {
|
|
36
|
-
selectItems.push(`${refSql(binding.from)} AS ${quoteIdent(field)}`);
|
|
37
|
-
columns.push(field);
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
columns,
|
|
41
|
-
selectItems
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
function compileProjectionJoins(projection) {
|
|
45
|
-
return projection.joins.map((join) => {
|
|
46
|
-
const keyword = join.type === "inner" ? "INNER JOIN" : "LEFT JOIN";
|
|
47
|
-
const on = join.on.map((c) => `${refSql(c.left)} = ${refSql(c.right)}`).join(" AND ");
|
|
48
|
-
return `${keyword} ${quoteIdent(join.table)} AS ${quoteIdent(join.key)} ON ${on}`;
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
/** `FROM <baseTable> AS "base"` — the alias every ref resolves against. */
|
|
52
|
-
function compileProjectionFrom(projection) {
|
|
53
|
-
return `FROM ${quoteIdent(projection.baseTable)} AS ${quoteIdent(require_projection.BASE_REF)}`;
|
|
54
|
-
}
|
|
55
|
-
//#endregion
|
|
1
|
+
const require_projection = require("./projection-Dz6_i1-w.cjs");
|
|
2
|
+
const require_projection_sql = require("./projection-sql-Cl1qIn8m.cjs");
|
|
56
3
|
//#region ../core/src/view-compiler.ts
|
|
57
4
|
/**
|
|
58
5
|
* View compiler (Phase 2) — turns a validated entity config into read-only
|
|
@@ -88,7 +35,7 @@ function quoteList(values) {
|
|
|
88
35
|
}
|
|
89
36
|
/** Compile one column predicate into a SQL boolean expression. */
|
|
90
37
|
function compilePredicate(table, column, value) {
|
|
91
|
-
const col = `${quoteIdent(table)}.${quoteIdent(column)}`;
|
|
38
|
+
const col = `${require_projection_sql.quoteIdent(table)}.${require_projection_sql.quoteIdent(column)}`;
|
|
92
39
|
if (value === null || typeof value !== "object") return `${col} = ${quoteLiteral(value)}`;
|
|
93
40
|
if ("eq" in value) return `${col} = ${quoteLiteral(value.eq)}`;
|
|
94
41
|
if ("ne" in value) return `${col} <> ${quoteLiteral(value.ne)}`;
|
|
@@ -121,33 +68,33 @@ function refParts(ref, defaultTable) {
|
|
|
121
68
|
}
|
|
122
69
|
function compileRelationship(relName, rel, source, related) {
|
|
123
70
|
const refCol = `${relName}_ref`;
|
|
124
|
-
const refAlias = quoteIdent(refCol);
|
|
71
|
+
const refAlias = require_projection_sql.quoteIdent(refCol);
|
|
125
72
|
const { via } = rel;
|
|
126
73
|
if ("fkOn" in via) {
|
|
127
74
|
const relatedTable = related?.source ?? "";
|
|
128
75
|
if (via.fkOn === "company") {
|
|
129
76
|
const { table, column } = refParts(via.column, relatedTable);
|
|
130
77
|
const relatedFilter = compileFilterConditions(table, related?.filter);
|
|
131
|
-
const on = [`${quoteIdent(table)}.${quoteIdent(column)} = ${quoteIdent(source.source)}.${quoteIdent(SOURCE_KEY)}`, ...relatedFilter].join(" AND ");
|
|
78
|
+
const on = [`${require_projection_sql.quoteIdent(table)}.${require_projection_sql.quoteIdent(column)} = ${require_projection_sql.quoteIdent(source.source)}.${require_projection_sql.quoteIdent(SOURCE_KEY)}`, ...relatedFilter].join(" AND ");
|
|
132
79
|
return {
|
|
133
|
-
clause: `LEFT JOIN ${quoteIdent(table)} ON ${on}`,
|
|
134
|
-
refSelect: `${quoteIdent(table)}.${quoteIdent(SOURCE_KEY)} AS ${refAlias}`,
|
|
80
|
+
clause: `LEFT JOIN ${require_projection_sql.quoteIdent(table)} ON ${on}`,
|
|
81
|
+
refSelect: `${require_projection_sql.quoteIdent(table)}.${require_projection_sql.quoteIdent(SOURCE_KEY)} AS ${refAlias}`,
|
|
135
82
|
refColumn: refCol
|
|
136
83
|
};
|
|
137
84
|
}
|
|
138
85
|
const { table, column } = refParts(via.column, source.source);
|
|
139
|
-
const on = [`${quoteIdent(relatedTable)}.${quoteIdent(SOURCE_KEY)} = ${quoteIdent(table)}.${quoteIdent(column)}`, ...compileFilterConditions(relatedTable, related?.filter)].join(" AND ");
|
|
86
|
+
const on = [`${require_projection_sql.quoteIdent(relatedTable)}.${require_projection_sql.quoteIdent(SOURCE_KEY)} = ${require_projection_sql.quoteIdent(table)}.${require_projection_sql.quoteIdent(column)}`, ...compileFilterConditions(relatedTable, related?.filter)].join(" AND ");
|
|
140
87
|
return {
|
|
141
|
-
clause: `LEFT JOIN ${quoteIdent(relatedTable)} ON ${on}`,
|
|
142
|
-
refSelect: `${quoteIdent(relatedTable)}.${quoteIdent(SOURCE_KEY)} AS ${refAlias}`,
|
|
88
|
+
clause: `LEFT JOIN ${require_projection_sql.quoteIdent(relatedTable)} ON ${on}`,
|
|
89
|
+
refSelect: `${require_projection_sql.quoteIdent(relatedTable)}.${require_projection_sql.quoteIdent(SOURCE_KEY)} AS ${refAlias}`,
|
|
143
90
|
refColumn: refCol
|
|
144
91
|
};
|
|
145
92
|
}
|
|
146
93
|
if ("through" in via) {
|
|
147
|
-
const on = `${quoteIdent(via.through)}.${quoteIdent(via.localKey)} = ${quoteIdent(source.source)}.${quoteIdent(SOURCE_KEY)}`;
|
|
94
|
+
const on = `${require_projection_sql.quoteIdent(via.through)}.${require_projection_sql.quoteIdent(via.localKey)} = ${require_projection_sql.quoteIdent(source.source)}.${require_projection_sql.quoteIdent(SOURCE_KEY)}`;
|
|
148
95
|
return {
|
|
149
|
-
clause: `LEFT JOIN ${quoteIdent(via.through)} ON ${on}`,
|
|
150
|
-
refSelect: `${quoteIdent(via.through)}.${quoteIdent(via.foreignKey)} AS ${refAlias}`,
|
|
96
|
+
clause: `LEFT JOIN ${require_projection_sql.quoteIdent(via.through)} ON ${on}`,
|
|
97
|
+
refSelect: `${require_projection_sql.quoteIdent(via.through)}.${require_projection_sql.quoteIdent(via.foreignKey)} AS ${refAlias}`,
|
|
151
98
|
refColumn: refCol
|
|
152
99
|
};
|
|
153
100
|
}
|
|
@@ -159,10 +106,10 @@ function compileRelationship(relName, rel, source, related) {
|
|
|
159
106
|
}
|
|
160
107
|
/** SELECT items for a legacy single-table entity: source key, then fields. */
|
|
161
108
|
function legacySelect(entity, src) {
|
|
162
|
-
const selectItems = [`${quoteIdent(src)}.${quoteIdent(SOURCE_KEY)} AS ${quoteIdent("id")}`];
|
|
109
|
+
const selectItems = [`${require_projection_sql.quoteIdent(src)}.${require_projection_sql.quoteIdent(SOURCE_KEY)} AS ${require_projection_sql.quoteIdent("id")}`];
|
|
163
110
|
const columns = ["id"];
|
|
164
111
|
for (const [field, column] of Object.entries(entity.fields)) {
|
|
165
|
-
selectItems.push(`${quoteIdent(src)}.${quoteIdent(column)} AS ${quoteIdent(field)}`);
|
|
112
|
+
selectItems.push(`${require_projection_sql.quoteIdent(src)}.${require_projection_sql.quoteIdent(column)} AS ${require_projection_sql.quoteIdent(field)}`);
|
|
166
113
|
columns.push(field);
|
|
167
114
|
}
|
|
168
115
|
return {
|
|
@@ -209,14 +156,14 @@ function compileEntityView(config, entityKey, options = {}) {
|
|
|
209
156
|
const src = entity.source;
|
|
210
157
|
const projection = entity.projection;
|
|
211
158
|
const filterTable = projection ? require_projection.BASE_REF : src;
|
|
212
|
-
const { selectItems, columns } = projection ? compileProjectionSelect(projection, SOURCE_KEY) : legacySelect(entity, src);
|
|
213
|
-
const joins = projection ? compileProjectionJoins(projection) : legacyJoins(config, entity, columns, selectItems);
|
|
159
|
+
const { selectItems, columns } = projection ? require_projection_sql.compileProjectionSelect(projection, SOURCE_KEY) : legacySelect(entity, src);
|
|
160
|
+
const joins = projection ? require_projection_sql.compileProjectionJoins(projection) : legacyJoins(config, entity, columns, selectItems);
|
|
214
161
|
const conditions = compileFilterConditions(filterTable, entity.filter);
|
|
215
|
-
if (options.orgScope) conditions.push(`${quoteIdent(filterTable)}.${quoteIdent(options.orgScope.column)} = ${quoteLiteral(options.orgScope.value)}`);
|
|
162
|
+
if (options.orgScope) conditions.push(`${require_projection_sql.quoteIdent(filterTable)}.${require_projection_sql.quoteIdent(options.orgScope.column)} = ${quoteLiteral(options.orgScope.value)}`);
|
|
216
163
|
const lines = [
|
|
217
|
-
options.securityInvoker ?? true ? `CREATE OR REPLACE VIEW ${quoteIdent(viewName)} WITH (security_invoker = true) AS` : `CREATE OR REPLACE VIEW ${quoteIdent(viewName)} AS`,
|
|
164
|
+
options.securityInvoker ?? true ? `CREATE OR REPLACE VIEW ${require_projection_sql.quoteIdent(viewName)} WITH (security_invoker = true) AS` : `CREATE OR REPLACE VIEW ${require_projection_sql.quoteIdent(viewName)} AS`,
|
|
218
165
|
`SELECT ${selectItems.join(", ")}`,
|
|
219
|
-
projection ? compileProjectionFrom(projection) : `FROM ${quoteIdent(src)}`,
|
|
166
|
+
projection ? require_projection_sql.compileProjectionFrom(projection) : `FROM ${require_projection_sql.quoteIdent(src)}`,
|
|
220
167
|
...joins
|
|
221
168
|
];
|
|
222
169
|
if (conditions.length > 0) lines.push(`WHERE ${conditions.join(" AND ")}`);
|
|
@@ -229,7 +176,7 @@ function compileEntityView(config, entityKey, options = {}) {
|
|
|
229
176
|
sourceTable: src,
|
|
230
177
|
columns,
|
|
231
178
|
sql,
|
|
232
|
-
dropSql: `DROP VIEW IF EXISTS ${quoteIdent(viewName)};`
|
|
179
|
+
dropSql: `DROP VIEW IF EXISTS ${require_projection_sql.quoteIdent(viewName)};`
|
|
233
180
|
};
|
|
234
181
|
}
|
|
235
182
|
/** Compile every entity in the config into its view, in declaration order. */
|
|
@@ -1,58 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* SQL generation for projection-backed entities.
|
|
5
|
-
*
|
|
6
|
-
* Base and joins are aliased by their ref key (`base`, then each join's `key`),
|
|
7
|
-
* which is what allows the same table to be joined twice and keeps generated
|
|
8
|
-
* SQL independent of host table names.
|
|
9
|
-
*
|
|
10
|
-
* Pure string generation — no DB access. Identifiers are escaped so output is
|
|
11
|
-
* deterministic and injection-safe.
|
|
12
|
-
*/
|
|
13
|
-
/** Quote a single identifier part, escaping embedded double-quotes. */
|
|
14
|
-
function quoteIdent(name) {
|
|
15
|
-
return `"${name.replace(/"/g, "\"\"")}"`;
|
|
16
|
-
}
|
|
17
|
-
function refSql(ref) {
|
|
18
|
-
return `${quoteIdent(ref.table)}.${quoteIdent(ref.column)}`;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* The identity expression, aliased as `id`. Composite identities concatenate
|
|
22
|
-
* their parts with `::`; validation guarantees every part is NOT NULL and
|
|
23
|
-
* non-text, which is what makes the concatenation collision-free.
|
|
24
|
-
*/
|
|
25
|
-
function identitySql(projection, basePkColumn) {
|
|
26
|
-
const refs = identityRefs(projection.identity, basePkColumn);
|
|
27
|
-
if (projection.identity.kind === "composite") return `CONCAT_WS('::', ${refs.map((ref) => `${refSql(ref)}::text`).join(", ")}) AS ${quoteIdent("id")}`;
|
|
28
|
-
const [ref] = refs;
|
|
29
|
-
if (!ref) throw new Error("projection identity resolved to no column");
|
|
30
|
-
return `${refSql(ref)} AS ${quoteIdent("id")}`;
|
|
31
|
-
}
|
|
32
|
-
function compileProjectionSelect(projection, basePkColumn) {
|
|
33
|
-
const selectItems = [identitySql(projection, basePkColumn)];
|
|
34
|
-
const columns = ["id"];
|
|
35
|
-
for (const [field, binding] of Object.entries(projection.fieldBindings)) {
|
|
36
|
-
selectItems.push(`${refSql(binding.from)} AS ${quoteIdent(field)}`);
|
|
37
|
-
columns.push(field);
|
|
38
|
-
}
|
|
39
|
-
return {
|
|
40
|
-
columns,
|
|
41
|
-
selectItems
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
function compileProjectionJoins(projection) {
|
|
45
|
-
return projection.joins.map((join) => {
|
|
46
|
-
const keyword = join.type === "inner" ? "INNER JOIN" : "LEFT JOIN";
|
|
47
|
-
const on = join.on.map((c) => `${refSql(c.left)} = ${refSql(c.right)}`).join(" AND ");
|
|
48
|
-
return `${keyword} ${quoteIdent(join.table)} AS ${quoteIdent(join.key)} ON ${on}`;
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
/** `FROM <baseTable> AS "base"` — the alias every ref resolves against. */
|
|
52
|
-
function compileProjectionFrom(projection) {
|
|
53
|
-
return `FROM ${quoteIdent(projection.baseTable)} AS ${quoteIdent(BASE_REF)}`;
|
|
54
|
-
}
|
|
55
|
-
//#endregion
|
|
1
|
+
import { t as BASE_REF } from "./projection-CSGJbZlZ.mjs";
|
|
2
|
+
import { a as quoteIdent, i as compileProjectionSelect, r as compileProjectionJoins, t as compileProjectionFrom } from "./projection-sql-BgnfiGqF.mjs";
|
|
56
3
|
//#region ../core/src/view-compiler.ts
|
|
57
4
|
/**
|
|
58
5
|
* View compiler (Phase 2) — turns a validated entity config into read-only
|
package/vite.d.cts
CHANGED
package/vite.d.mts
CHANGED