drizzle-cube 0.4.28 → 0.4.29

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.
@@ -857,8 +857,8 @@ export declare interface CubeDiscoveryResult {
857
857
  * Type-safe cube join definition with lazy loading support
858
858
  */
859
859
  export declare interface CubeJoin {
860
- /** Target cube reference - lazy loaded to avoid circular dependencies */
861
- targetCube: Cube | (() => Cube);
860
+ /** Target cube reference - lazy loaded to avoid circular dependencies, or string name resolved from registry */
861
+ targetCube: Cube | (() => Cube) | string;
862
862
  /** Semantic relationship - determines join behavior */
863
863
  relationship: CubeRelationship;
864
864
  /** Array of join conditions - supports multi-column joins */
@@ -4081,9 +4081,12 @@ export declare interface QueryWarning {
4081
4081
  export declare type QueryWarningSeverity = 'info' | 'warning' | 'error';
4082
4082
 
4083
4083
  /**
4084
- * Resolve cube reference (handles both direct and lazy references)
4084
+ * Resolve cube reference (handles direct, lazy, and string name references)
4085
+ *
4086
+ * String references are resolved from the optional `cubes` registry map.
4087
+ * Returns `null` when a string ref cannot be resolved (logs a warning).
4085
4088
  */
4086
- export declare function resolveCubeReference(ref: Cube | (() => Cube)): Cube;
4089
+ export declare function resolveCubeReference(ref: Cube | (() => Cube) | string, cubes?: Map<string, Cube>): Cube | null;
4087
4090
 
4088
4091
  /**
4089
4092
  * Resolved measure SQL builders
@@ -4414,6 +4417,12 @@ export declare class SemanticLayerCompiler {
4414
4417
  * Validates calculated measures during registration
4415
4418
  */
4416
4419
  registerCube(cube: Cube): void;
4420
+ /**
4421
+ * Validate that all string-based cube references in joins resolve to registered cubes.
4422
+ * Call after all cubes are registered for strict startup validation.
4423
+ * Throws an error listing all unresolved references.
4424
+ */
4425
+ validateCubeReferences(): void;
4417
4426
  /**
4418
4427
  * Validate calculated measures in a cube
4419
4428
  * Checks template syntax, dependency existence, and circular dependencies
@@ -2007,7 +2007,17 @@ function Gt(i, e, t) {
2007
2007
  return gt(i, e);
2008
2008
  throw new Error("Unable to determine database engine type. Please specify engineType parameter.");
2009
2009
  }
2010
- function J(i) {
2010
+ function k(i, e) {
2011
+ if (typeof i == "string") {
2012
+ if (!e)
2013
+ return console.warn(
2014
+ `[drizzle-cube] Cannot resolve string cube reference '${i}': no cube registry provided. Join will be skipped.`
2015
+ ), null;
2016
+ const t = e.get(i);
2017
+ return t || (console.warn(
2018
+ `[drizzle-cube] Cannot resolve cube reference '${i}': no cube with that name is registered. Registered cubes: ${Array.from(e.keys()).join(", ") || "(none)"}. Join will be skipped.`
2019
+ ), null);
2020
+ }
2011
2021
  return typeof i == "function" ? i() : i;
2012
2022
  }
2013
2023
  function Ve(i) {
@@ -3848,7 +3858,9 @@ class De {
3848
3858
  if (s.joins)
3849
3859
  for (const [, n] of Object.entries(s.joins)) {
3850
3860
  if (n.relationship === "belongsToMany") continue;
3851
- const E = J(n.targetCube).name;
3861
+ const r = k(n.targetCube, this.cubes);
3862
+ if (!r) continue;
3863
+ const E = r.name;
3852
3864
  let a = e.get(E);
3853
3865
  a || (a = [], e.set(E, a)), a.push({ definingCube: t, joinDef: n });
3854
3866
  }
@@ -3876,7 +3888,9 @@ class De {
3876
3888
  const { cube: A, path: o } = E.shift(), R = this.cubes.get(A);
3877
3889
  if (R?.joins)
3878
3890
  for (const [, l] of Object.entries(R.joins)) {
3879
- const I = J(l.targetCube).name;
3891
+ const N = k(l.targetCube, this.cubes);
3892
+ if (!N) continue;
3893
+ const I = N.name;
3880
3894
  if (a.has(I))
3881
3895
  continue;
3882
3896
  const u = [
@@ -4011,7 +4025,9 @@ class De {
4011
4025
  const R = this.cubes.get(a);
4012
4026
  if (R?.joins)
4013
4027
  for (const [, l] of Object.entries(R.joins)) {
4014
- const I = J(l.targetCube).name;
4028
+ const N = k(l.targetCube, this.cubes);
4029
+ if (!N) continue;
4030
+ const I = N.name;
4015
4031
  if (o.has(I))
4016
4032
  continue;
4017
4033
  const u = [
@@ -4081,7 +4097,9 @@ class De {
4081
4097
  const { cube: E, path: a } = n.shift(), A = this.cubes.get(E);
4082
4098
  if (A?.joins)
4083
4099
  for (const [, o] of Object.entries(A.joins)) {
4084
- const S = J(o.targetCube).name;
4100
+ const R = k(o.targetCube, this.cubes);
4101
+ if (!R) continue;
4102
+ const S = R.name;
4085
4103
  if (r.has(S)) continue;
4086
4104
  const l = [
4087
4105
  ...a,
@@ -4122,7 +4140,9 @@ class De {
4122
4140
  const n = s.shift(), r = this.cubes.get(n);
4123
4141
  if (r?.joins)
4124
4142
  for (const [, a] of Object.entries(r.joins)) {
4125
- const o = J(a.targetCube).name;
4143
+ const A = k(a.targetCube, this.cubes);
4144
+ if (!A) continue;
4145
+ const o = A.name;
4126
4146
  t.has(o) || (t.add(o), s.push(o));
4127
4147
  }
4128
4148
  const E = this.reverseIndex.get(n) || [];
@@ -4300,7 +4320,7 @@ class Un {
4300
4320
  const R = this.collectPathHintCubes(r), S = /* @__PURE__ */ new Set();
4301
4321
  for (const N of o) {
4302
4322
  if (N === t.name) continue;
4303
- this.findHasManyJoinDef(t, N) && S.add(N);
4323
+ this.findHasManyJoinDef(t, N, e) && S.add(N);
4304
4324
  }
4305
4325
  const l = s.filter((N) => N !== t.name);
4306
4326
  for (const N of l) {
@@ -4470,11 +4490,12 @@ class Un {
4470
4490
  */
4471
4491
  findJoinInfoToCube(e, t) {
4472
4492
  for (const [, s] of e)
4473
- if (s.name !== t && s.joins) {
4474
- for (const [, n] of Object.entries(s.joins))
4475
- if (J(n.targetCube).name === t)
4493
+ if (s.name !== t && s.joins)
4494
+ for (const [, n] of Object.entries(s.joins)) {
4495
+ const r = k(n.targetCube, e);
4496
+ if (r && r.name === t)
4476
4497
  return { sourceCube: s, joinDef: n };
4477
- }
4498
+ }
4478
4499
  return null;
4479
4500
  }
4480
4501
  /**
@@ -4575,23 +4596,26 @@ class Un {
4575
4596
  * and to search from any source cube, not just the primary
4576
4597
  */
4577
4598
  findJoinInfoForCube(e, t, s) {
4578
- if (t.joins) {
4579
- for (const [, r] of Object.entries(t.joins))
4580
- if (J(r.targetCube).name === s)
4599
+ if (t.joins)
4600
+ for (const [, r] of Object.entries(t.joins)) {
4601
+ const E = k(r.targetCube, e);
4602
+ if (E && E.name === s)
4581
4603
  return { sourceCube: t, joinDef: r };
4582
- }
4604
+ }
4583
4605
  const n = e.get(s);
4584
- if (n?.joins) {
4585
- for (const [, r] of Object.entries(n.joins))
4586
- if (J(r.targetCube).name === t.name)
4606
+ if (n?.joins)
4607
+ for (const [, r] of Object.entries(n.joins)) {
4608
+ const E = k(r.targetCube, e);
4609
+ if (E && E.name === t.name)
4587
4610
  return { sourceCube: n, joinDef: r, reversed: !0 };
4588
- }
4611
+ }
4589
4612
  for (const [, r] of e)
4590
- if (!(r.name === t.name || r.name === s) && r.joins) {
4591
- for (const [, E] of Object.entries(r.joins))
4592
- if (J(E.targetCube).name === s)
4613
+ if (!(r.name === t.name || r.name === s) && r.joins)
4614
+ for (const [, E] of Object.entries(r.joins)) {
4615
+ const a = k(E.targetCube, e);
4616
+ if (a && a.name === s)
4593
4617
  return { sourceCube: r, joinDef: E };
4594
- }
4618
+ }
4595
4619
  return null;
4596
4620
  }
4597
4621
  /**
@@ -4626,7 +4650,9 @@ class Un {
4626
4650
  }
4627
4651
  if (e.joins)
4628
4652
  for (const [, E] of Object.entries(e.joins)) {
4629
- const A = J(E.targetCube).name;
4653
+ const a = k(E.targetCube, s);
4654
+ if (!a) continue;
4655
+ const A = a.name;
4630
4656
  if (r.has(A)) {
4631
4657
  let o;
4632
4658
  E.relationship === "belongsToMany" && E.through ? o = E.through.sourceKey.map((R) => ({
@@ -4683,12 +4709,14 @@ class Un {
4683
4709
  /**
4684
4710
  * Find hasMany join definition from primary cube to target cube
4685
4711
  */
4686
- findHasManyJoinDef(e, t) {
4712
+ findHasManyJoinDef(e, t, s) {
4687
4713
  if (!e.joins)
4688
4714
  return null;
4689
- for (const [, s] of Object.entries(e.joins))
4690
- if (J(s.targetCube).name === t && s.relationship === "hasMany")
4691
- return s;
4715
+ for (const [, n] of Object.entries(e.joins)) {
4716
+ const r = k(n.targetCube, s);
4717
+ if (r && r.name === t && n.relationship === "hasMany")
4718
+ return n;
4719
+ }
4692
4720
  return null;
4693
4721
  }
4694
4722
  /**
@@ -4713,9 +4741,10 @@ class Un {
4713
4741
  for (const E of r) {
4714
4742
  if (E === t.name) continue;
4715
4743
  const a = s.get(E);
4716
- if (a?.joins) {
4717
- for (const [, A] of Object.entries(a.joins))
4718
- if (J(A.targetCube).name === t.name && A.relationship === "hasMany") {
4744
+ if (a?.joins)
4745
+ for (const [, A] of Object.entries(a.joins)) {
4746
+ const o = k(A.targetCube, s);
4747
+ if (o && o.name === t.name && A.relationship === "hasMany") {
4719
4748
  const R = this.extractFiltersForCube(e.filters, E), S = this.extractTimeDimensionFiltersForCube(e, E), l = [...R, ...S];
4720
4749
  l.length > 0 && A.on.length > 0 && n.push({
4721
4750
  sourceCube: a,
@@ -4729,7 +4758,7 @@ class Un {
4729
4758
  }))
4730
4759
  });
4731
4760
  }
4732
- }
4761
+ }
4733
4762
  }
4734
4763
  return n;
4735
4764
  }
@@ -7248,7 +7277,7 @@ class Vn {
7248
7277
  return null;
7249
7278
  const A = Array.from(n);
7250
7279
  if (!A.every(
7251
- (I) => this.hasDirectJoinToSharedDimension(t.get(I), E)
7280
+ (I) => this.hasDirectJoinToSharedDimension(t.get(I), E, t)
7252
7281
  ))
7253
7282
  return null;
7254
7283
  const R = this.buildDimensionRefs(e, t), S = this.buildTimeDimensionRefs(e, t), l = {
@@ -7277,12 +7306,12 @@ class Vn {
7277
7306
  mergeStrategy: "fullJoin"
7278
7307
  };
7279
7308
  }
7280
- hasDirectJoinToSharedDimension(e, t) {
7309
+ hasDirectJoinToSharedDimension(e, t, s) {
7281
7310
  if (!e?.joins)
7282
7311
  return !1;
7283
- for (const [, s] of Object.entries(e.joins)) {
7284
- const n = s.targetCube, r = typeof n == "function" ? n() : n;
7285
- if (!(!r || r.name !== t) && (s.relationship === "belongsTo" || s.relationship === "hasOne"))
7312
+ for (const [, n] of Object.entries(e.joins)) {
7313
+ const r = k(n.targetCube, s);
7314
+ if (!(!r || r.name !== t) && (n.relationship === "belongsTo" || n.relationship === "hasOne"))
7286
7315
  return !0;
7287
7316
  }
7288
7317
  return !1;
@@ -8220,19 +8249,19 @@ class Zn {
8220
8249
  const o = `${r.multipliedCubeName.toLowerCase()}_keys`, R = `${r.multipliedCubeName.toLowerCase()}_pk_agg`, S = {}, l = [];
8221
8250
  if (t.dimensions)
8222
8251
  for (const D of t.dimensions) {
8223
- const [U, B] = D.split("."), w = E.get(U), k = w?.dimensions?.[B];
8224
- if (!w || !k)
8252
+ const [U, B] = D.split("."), w = E.get(U), J = w?.dimensions?.[B];
8253
+ if (!w || !J)
8225
8254
  return null;
8226
- const v = H(k.sql, s);
8255
+ const v = H(J.sql, s);
8227
8256
  S[D] = T`${v}`.as(D), l.push(v);
8228
8257
  }
8229
8258
  if (t.timeDimensions)
8230
8259
  for (const D of t.timeDimensions) {
8231
- const [U, B] = D.dimension.split("."), w = E.get(U), k = w?.dimensions?.[B];
8232
- if (!w || !k)
8260
+ const [U, B] = D.dimension.split("."), w = E.get(U), J = w?.dimensions?.[B];
8261
+ if (!w || !J)
8233
8262
  return null;
8234
8263
  const v = n.queryBuilder.buildTimeDimensionExpression(
8235
- k.sql,
8264
+ J.sql,
8236
8265
  D.granularity,
8237
8266
  s
8238
8267
  );
@@ -8325,8 +8354,8 @@ class Zn {
8325
8354
  if (!y.has(U)) continue;
8326
8355
  const B = a.measures?.[U];
8327
8356
  if (!B?.sql) return null;
8328
- const w = H(B.sql, s), k = `__avg_sum__${U}`, v = `__avg_count__${U}`;
8329
- h[k] = T`sum(${w})`.as(k), h[v] = T`count(${w})`.as(v);
8357
+ const w = H(B.sql, s), J = `__avg_sum__${U}`, v = `__avg_count__${U}`;
8358
+ h[J] = T`sum(${w})`.as(J), h[v] = T`count(${w})`.as(v);
8330
8359
  }
8331
8360
  let b = s.db.select(h).from(m.from);
8332
8361
  const Y = [];
@@ -8350,9 +8379,9 @@ class Zn {
8350
8379
  );
8351
8380
  }
8352
8381
  for (const D of I) {
8353
- const [U, B] = D.split("."), k = E.get(U)?.measures?.[B], v = `__reg__${D.replace(".", "__")}`;
8382
+ const [U, B] = D.split("."), J = E.get(U)?.measures?.[B], v = `__reg__${D.replace(".", "__")}`;
8354
8383
  z[D] = this.buildKeysOuterAggregation(
8355
- k?.type ?? "sum",
8384
+ J?.type ?? "sum",
8356
8385
  o,
8357
8386
  v,
8358
8387
  D
@@ -29236,6 +29265,22 @@ class _t {
29236
29265
  registerCube(e) {
29237
29266
  this.validateCalculatedMeasures(e), new oe(this.cubes).populateDependencies(e), this.cubes.set(e.name, e), this.invalidateMetadataCache();
29238
29267
  }
29268
+ /**
29269
+ * Validate that all string-based cube references in joins resolve to registered cubes.
29270
+ * Call after all cubes are registered for strict startup validation.
29271
+ * Throws an error listing all unresolved references.
29272
+ */
29273
+ validateCubeReferences() {
29274
+ const e = [];
29275
+ for (const [t, s] of this.cubes)
29276
+ if (s.joins)
29277
+ for (const [n, r] of Object.entries(s.joins))
29278
+ typeof r.targetCube == "string" && !this.cubes.has(r.targetCube) && e.push(`${t}.joins.${n}: target cube '${r.targetCube}' is not registered`);
29279
+ if (e.length > 0)
29280
+ throw new Error(`Unresolved cube references:
29281
+ ${e.map((t) => ` - ${t}`).join(`
29282
+ `)}`);
29283
+ }
29239
29284
  /**
29240
29285
  * Validate calculated measures in a cube
29241
29286
  * Checks template syntax, dependency existence, and circular dependencies
@@ -29399,8 +29444,8 @@ ${t.join(`
29399
29444
  const E = [];
29400
29445
  if (e.joins)
29401
29446
  for (const [, o] of Object.entries(e.joins)) {
29402
- const R = typeof o.targetCube == "function" ? o.targetCube() : o.targetCube;
29403
- E.push({
29447
+ const R = k(o.targetCube, this.cubes);
29448
+ R && E.push({
29404
29449
  targetCube: R.name,
29405
29450
  relationship: o.relationship,
29406
29451
  joinFields: o.on.map((S) => ({
@@ -33359,7 +33404,7 @@ ${i.systemContext}`);
33359
33404
  };
33360
33405
  continue;
33361
33406
  }
33362
- const k = Date.now();
33407
+ const J = Date.now();
33363
33408
  try {
33364
33409
  const v = await w(U);
33365
33410
  v.sideEffect && (yield v.sideEffect), Ae.push({
@@ -33378,7 +33423,7 @@ ${i.systemContext}`);
33378
33423
  toolName: D,
33379
33424
  toolUseId: B,
33380
33425
  isError: !!v.isError,
33381
- durationMs: Date.now() - k
33426
+ durationMs: Date.now() - J
33382
33427
  });
33383
33428
  } catch {
33384
33429
  }
@@ -33400,7 +33445,7 @@ ${i.systemContext}`);
33400
33445
  toolName: D,
33401
33446
  toolUseId: B,
33402
33447
  isError: !0,
33403
- durationMs: Date.now() - k
33448
+ durationMs: Date.now() - J
33404
33449
  });
33405
33450
  } catch {
33406
33451
  }
@@ -34494,7 +34539,7 @@ export {
34494
34539
  $T as getToolDefinitions,
34495
34540
  OA as handleAgentChat,
34496
34541
  mn as normalizeQuery,
34497
- J as resolveCubeReference,
34542
+ k as resolveCubeReference,
34498
34543
  H as resolveSqlExpression,
34499
34544
  cA as suggestQuery
34500
34545
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drizzle-cube",
3
- "version": "0.4.28",
3
+ "version": "0.4.29",
4
4
  "description": "Drizzle ORM-first semantic layer with Cube.js compatibility. Type-safe analytics and dashboards with SQL injection protection.",
5
5
  "main": "./dist/server/index.js",
6
6
  "types": "./dist/server/index.d.ts",