@514labs/moose-lib 0.6.527 → 0.6.528

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.
Files changed (37) hide show
  1. package/dist/browserCompatible.d.mts +2 -2
  2. package/dist/browserCompatible.d.ts +2 -2
  3. package/dist/browserCompatible.js +393 -2
  4. package/dist/browserCompatible.js.map +1 -1
  5. package/dist/browserCompatible.mjs +389 -2
  6. package/dist/browserCompatible.mjs.map +1 -1
  7. package/dist/compilerPlugin.js +2 -1
  8. package/dist/compilerPlugin.js.map +1 -1
  9. package/dist/compilerPlugin.mjs +2 -1
  10. package/dist/compilerPlugin.mjs.map +1 -1
  11. package/dist/dmv2/index.d.mts +2 -2
  12. package/dist/dmv2/index.d.ts +2 -2
  13. package/dist/dmv2/index.js +393 -2
  14. package/dist/dmv2/index.js.map +1 -1
  15. package/dist/dmv2/index.mjs +389 -2
  16. package/dist/dmv2/index.mjs.map +1 -1
  17. package/dist/{index-k_kpRxE3.d.mts → index-BTIlwBBZ.d.mts} +13 -2
  18. package/dist/{index-7uxZbwmY.d.ts → index-w7pvlv3c.d.ts} +13 -2
  19. package/dist/index.d.mts +4 -4
  20. package/dist/index.d.ts +4 -4
  21. package/dist/index.js +394 -2
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.mjs +390 -2
  24. package/dist/index.mjs.map +1 -1
  25. package/dist/moose-runner.js +36 -3
  26. package/dist/moose-runner.js.map +1 -1
  27. package/dist/moose-runner.mjs +36 -3
  28. package/dist/moose-runner.mjs.map +1 -1
  29. package/dist/{view-BCWJcLF6.d.mts → query-client-6YrlC3Df.d.mts} +420 -80
  30. package/dist/{view-BCWJcLF6.d.ts → query-client-6YrlC3Df.d.ts} +420 -80
  31. package/dist/testing/index.d.mts +1 -1
  32. package/dist/testing/index.d.ts +1 -1
  33. package/dist/testing/index.js +10 -1
  34. package/dist/testing/index.js.map +1 -1
  35. package/dist/testing/index.mjs +10 -1
  36. package/dist/testing/index.mjs.map +1 -1
  37. package/package.json +1 -1
@@ -643,6 +643,7 @@ var quoteIdentifier = (name) => {
643
643
  };
644
644
  var isTable = (value) => typeof value === "object" && value !== null && "kind" in value && value.kind === "OlapTable";
645
645
  var isView = (value) => typeof value === "object" && value !== null && "kind" in value && value.kind === "View";
646
+ var isDictionary = (value) => typeof value === "object" && value !== null && "kind" in value && value.kind === "OlapDictionary";
646
647
  var isColumn = (value) => typeof value === "object" && value !== null && !("kind" in value) && "name" in value && "annotations" in value;
647
648
  function sqlImpl(strings, ...values) {
648
649
  return new Sql(strings, values);
@@ -669,7 +670,7 @@ var Sql = class _Sql {
669
670
  );
670
671
  }
671
672
  const valuesLength = rawValues.reduce(
672
- (len, value) => len + (instanceofSql(value) ? value.values.length : isColumn(value) || isTable(value) || isView(value) ? 0 : 1),
673
+ (len, value) => len + (instanceofSql(value) ? value.values.length : isColumn(value) || isTable(value) || isView(value) || isDictionary(value) ? 0 : 1),
673
674
  0
674
675
  );
675
676
  this.values = new Array(valuesLength);
@@ -716,6 +717,14 @@ var Sql = class _Sql {
716
717
  this.strings[pos] += `\`${child.name}\``;
717
718
  }
718
719
  this.strings[pos] += rawString;
720
+ } else if (isDictionary(child)) {
721
+ if (/\b(?:FROM|JOIN)\s*$/i.test(this.strings[pos])) {
722
+ console.warn(
723
+ `OlapDictionary '${child.getQualifiedName()}' interpolated after FROM/JOIN in sql tag. Dictionaries render as string literals (e.g. 'db.dict_name') for use with dictGet(), not as table identifiers. ClickHouse dictionaries cannot be queried directly with FROM/JOIN.`
724
+ );
725
+ }
726
+ this.strings[pos] += `'${child.getQualifiedName().replace(/'/g, "''")}'`;
727
+ this.strings[pos] += rawString;
719
728
  } else {
720
729
  this.values[pos++] = child;
721
730
  this.strings[pos] = rawString;
@@ -983,6 +992,12 @@ function getSelectRowPolicies() {
983
992
  function getSelectRowPolicy(name) {
984
993
  return getMooseInternal().selectRowPolicies.get(name);
985
994
  }
995
+ function getOlapDictionaries() {
996
+ return getMooseInternal().olapDictionaries;
997
+ }
998
+ function getOlapDictionary(name) {
999
+ return getMooseInternal().olapDictionaries.get(name);
1000
+ }
986
1001
 
987
1002
  // src/consumption-apis/standalone.ts
988
1003
  import { AsyncLocalStorage } from "async_hooks";
@@ -1180,7 +1195,8 @@ function createRegistryFrom(existing) {
1180
1195
  webApps: toTrackingMap(existing?.webApps),
1181
1196
  materializedViews: toTrackingMap(existing?.materializedViews),
1182
1197
  views: toTrackingMap(existing?.views),
1183
- selectRowPolicies: toTrackingMap(existing?.selectRowPolicies)
1198
+ selectRowPolicies: toTrackingMap(existing?.selectRowPolicies),
1199
+ olapDictionaries: toTrackingMap(existing?.olapDictionaries)
1184
1200
  };
1185
1201
  }
1186
1202
  var moose_internal = {
@@ -1220,6 +1236,10 @@ var moose_internal = {
1220
1236
  selectRowPolicies: new MutationTrackingMap(
1221
1237
  void 0,
1222
1238
  markRegistryMutated
1239
+ ),
1240
+ olapDictionaries: new MutationTrackingMap(
1241
+ void 0,
1242
+ markRegistryMutated
1223
1243
  )
1224
1244
  };
1225
1245
  var defaultRetentionPeriod = 60 * 60 * 24 * 7;
@@ -3101,6 +3121,369 @@ var LifeCycle = /* @__PURE__ */ ((LifeCycle2) => {
3101
3121
  return LifeCycle2;
3102
3122
  })(LifeCycle || {});
3103
3123
 
3124
+ // src/dmv2/sdk/olapDictionary.ts
3125
+ var COMPLEX_KEY_LAYOUTS = /* @__PURE__ */ new Set([
3126
+ "COMPLEX_KEY_HASHED",
3127
+ "COMPLEX_KEY_SPARSE_HASHED",
3128
+ "COMPLEX_KEY_HASHED_ARRAY",
3129
+ "COMPLEX_KEY_CACHE",
3130
+ "COMPLEX_KEY_SSD_CACHE",
3131
+ "COMPLEX_KEY_DIRECT"
3132
+ ]);
3133
+ function dataTypeToString(dataType) {
3134
+ if (typeof dataType === "string") {
3135
+ return dataType;
3136
+ }
3137
+ if (typeof dataType === "object" && dataType !== null) {
3138
+ if ("nullable" in dataType) {
3139
+ const inner = dataTypeToString(
3140
+ dataType.nullable
3141
+ );
3142
+ return `Nullable(${inner})`;
3143
+ }
3144
+ if ("elementType" in dataType) {
3145
+ const arr = dataType;
3146
+ const inner = dataTypeToString(arr.elementType);
3147
+ return arr.elementNullable ? `Array(Nullable(${inner}))` : `Array(${inner})`;
3148
+ }
3149
+ if ("name" in dataType && "values" in dataType) {
3150
+ const d = dataType;
3151
+ const entries = d.values.map(
3152
+ (v) => v.value.String !== void 0 ? `'${v.name}' = '${v.value.String}'` : `'${v.name}' = ${v.value.Int ?? 0}`
3153
+ ).join(", ");
3154
+ return `${d.name}(${entries})`;
3155
+ }
3156
+ }
3157
+ throw new Error(
3158
+ `OlapDictionary: unsupported column data type for dictionary attribute: ${JSON.stringify(dataType)}. Dictionaries only support scalar value types (strings, integers, floats, booleans, dates, Nullable wrappers, Array wrappers, and Enum types).`
3159
+ );
3160
+ }
3161
+ function buildDictionaryColumns(columns, overrides) {
3162
+ return columns.map((col) => {
3163
+ const override = overrides?.[col.name] ?? {};
3164
+ const result = {
3165
+ name: col.name,
3166
+ typeString: dataTypeToString(col.data_type)
3167
+ };
3168
+ if (override.defaultValue !== void 0)
3169
+ result.defaultValue = override.defaultValue;
3170
+ if (override.expression !== void 0)
3171
+ result.expression = override.expression;
3172
+ if (override.isInjective !== void 0)
3173
+ result.isInjective = override.isInjective;
3174
+ if (override.isHierarchical !== void 0)
3175
+ result.isHierarchical = override.isHierarchical;
3176
+ if (override.isObjectId !== void 0)
3177
+ result.isObjectId = override.isObjectId;
3178
+ if (override.comment !== void 0) result.comment = override.comment;
3179
+ return result;
3180
+ });
3181
+ }
3182
+ function serializeLifetime(lifetime) {
3183
+ if (typeof lifetime === "number") {
3184
+ if (!Number.isFinite(lifetime) || lifetime < 0 || !Number.isInteger(lifetime)) {
3185
+ throw new Error(
3186
+ `OlapDictionary: lifetime must be a finite non-negative integer (got ${lifetime}).`
3187
+ );
3188
+ }
3189
+ if (lifetime === 0) {
3190
+ return { type: "STATIC" };
3191
+ }
3192
+ return { type: "SINGLE", seconds: lifetime };
3193
+ }
3194
+ if (!Number.isFinite(lifetime.min) || !Number.isFinite(lifetime.max) || !Number.isInteger(lifetime.min) || !Number.isInteger(lifetime.max) || lifetime.min < 0 || lifetime.max < lifetime.min) {
3195
+ throw new Error(
3196
+ `OlapDictionary: lifetime range must use finite non-negative integers with min <= max (got min=${lifetime.min}, max=${lifetime.max}).`
3197
+ );
3198
+ }
3199
+ if (lifetime.min === 0 && lifetime.max === 0) {
3200
+ return { type: "STATIC" };
3201
+ }
3202
+ if (lifetime.min === lifetime.max) {
3203
+ return { type: "SINGLE", seconds: lifetime.min };
3204
+ }
3205
+ return { type: "RANGE", min: lifetime.min, max: lifetime.max };
3206
+ }
3207
+ function serializeLayout(layout) {
3208
+ const { type, ...rest } = layout;
3209
+ const snakeCaseFields = {};
3210
+ for (const [key, value] of Object.entries(rest)) {
3211
+ if (value === void 0) continue;
3212
+ const snake = key.replace(/([A-Z])/g, "_$1").toLowerCase();
3213
+ snakeCaseFields[snake] = value;
3214
+ }
3215
+ return { type, ...snakeCaseFields };
3216
+ }
3217
+ function externalTypeToRust(type) {
3218
+ const mapping = {
3219
+ http: "HTTP",
3220
+ clickhouse: "CLICK_HOUSE",
3221
+ mysql: "MYSQL",
3222
+ postgresql: "POSTGRESQL",
3223
+ redis: "REDIS",
3224
+ mongodb: "MONGODB",
3225
+ executable: "EXECUTABLE",
3226
+ s3: "S3"
3227
+ };
3228
+ return mapping[type];
3229
+ }
3230
+ function serializeExternalSource(ext) {
3231
+ const { type, ...rest } = ext;
3232
+ const inner = {
3233
+ source_type: externalTypeToRust(type)
3234
+ };
3235
+ for (const [key, value] of Object.entries(rest)) {
3236
+ inner[key] = value;
3237
+ }
3238
+ return {
3239
+ type: "EXTERNAL",
3240
+ externalSource: inner
3241
+ };
3242
+ }
3243
+ function serializeSource(config) {
3244
+ if (config.sourceTable !== void 0) {
3245
+ const table = config.sourceTable;
3246
+ if (table instanceof OlapTable) {
3247
+ return {
3248
+ type: "TABLE",
3249
+ table: table.generateTableName(),
3250
+ database: table.config.database,
3251
+ ...config.invalidateQuery !== void 0 ? { invalidateQuery: config.invalidateQuery } : {}
3252
+ };
3253
+ } else {
3254
+ return {
3255
+ type: "TABLE",
3256
+ table: table.name,
3257
+ database: table.database,
3258
+ ...config.invalidateQuery !== void 0 ? { invalidateQuery: config.invalidateQuery } : {}
3259
+ };
3260
+ }
3261
+ }
3262
+ if (config.sourceQuery !== void 0) {
3263
+ return {
3264
+ type: "QUERY",
3265
+ query: toStaticQuery(config.sourceQuery),
3266
+ ...config.invalidateQuery !== void 0 ? { invalidateQuery: config.invalidateQuery } : {}
3267
+ };
3268
+ }
3269
+ if (config.externalSource !== void 0) {
3270
+ return serializeExternalSource(config.externalSource);
3271
+ }
3272
+ throw new Error(
3273
+ "OlapDictionary: no source configured (unreachable after validation)"
3274
+ );
3275
+ }
3276
+ var OlapDictionary = class {
3277
+ /** @internal */
3278
+ kind = "OlapDictionary";
3279
+ /** Dictionary name */
3280
+ name;
3281
+ /** User configuration */
3282
+ config;
3283
+ /** Compiler-injected columns (name + type from T) */
3284
+ _columns;
3285
+ /** Serialized column list (DictionaryColumn JSON objects) */
3286
+ serializedColumns;
3287
+ constructor(name, config, _schema, columns) {
3288
+ if (_schema === void 0 || columns === void 0) {
3289
+ throw new Error(
3290
+ "Supply the type param T so that the schema is inserted by the compiler plugin."
3291
+ );
3292
+ }
3293
+ this.name = name;
3294
+ this.config = config;
3295
+ this._columns = columns;
3296
+ const sourcesSet = [
3297
+ config.sourceTable !== void 0,
3298
+ config.sourceQuery !== void 0,
3299
+ config.externalSource !== void 0
3300
+ ].filter(Boolean).length;
3301
+ if (sourcesSet === 0) {
3302
+ throw new Error(
3303
+ `OlapDictionary '${name}': exactly one of sourceTable, sourceQuery, or externalSource must be set (none provided).`
3304
+ );
3305
+ }
3306
+ if (sourcesSet > 1) {
3307
+ throw new Error(
3308
+ `OlapDictionary '${name}': exactly one of sourceTable, sourceQuery, or externalSource must be set (${sourcesSet} provided).`
3309
+ );
3310
+ }
3311
+ if (config.sourceQuery !== void 0 && !config.sourceTables?.length) {
3312
+ throw new Error(
3313
+ `OlapDictionary '${name}': sourceQuery requires sourceTables to be set for dependency tracking.`
3314
+ );
3315
+ }
3316
+ if (config.sourceQuery !== void 0) {
3317
+ if (!toStaticQuery(config.sourceQuery).trim()) {
3318
+ throw new Error(
3319
+ `OlapDictionary '${name}': sourceQuery must not be blank.`
3320
+ );
3321
+ }
3322
+ }
3323
+ if (!config.primaryKey.length) {
3324
+ throw new Error(
3325
+ `OlapDictionary '${name}': primaryKey must contain at least one column name.`
3326
+ );
3327
+ }
3328
+ if (!COMPLEX_KEY_LAYOUTS.has(config.layout.type)) {
3329
+ if (config.primaryKey.length !== 1) {
3330
+ throw new Error(
3331
+ `OlapDictionary '${name}': layout '${config.layout.type}' requires exactly 1 primary key column (got ${config.primaryKey.length}). Use a COMPLEX_KEY_* layout for string or multi-column keys.`
3332
+ );
3333
+ }
3334
+ }
3335
+ this.serializedColumns = buildDictionaryColumns(
3336
+ columns,
3337
+ config.columns
3338
+ );
3339
+ if (!config.metadata?.source) {
3340
+ const stack = new Error().stack;
3341
+ const sourceInfo = getSourceFileFromStack(stack);
3342
+ if (sourceInfo) {
3343
+ this.config.metadata = {
3344
+ ...config.metadata,
3345
+ source: { file: sourceInfo }
3346
+ };
3347
+ }
3348
+ }
3349
+ const olapDictionaries = getMooseInternal().olapDictionaries;
3350
+ if (!isClientOnlyMode() && olapDictionaries.has(name)) {
3351
+ throw new Error(`OlapDictionary with name '${name}' already exists`);
3352
+ }
3353
+ olapDictionaries.set(name, this);
3354
+ }
3355
+ /**
3356
+ * Returns the qualified dictionary name for use in dictGet calls.
3357
+ * Format: `database.name` if database is set, otherwise just `name`.
3358
+ */
3359
+ getQualifiedName() {
3360
+ if (this.config.database) {
3361
+ return `${this.config.database}.${this.name}`;
3362
+ }
3363
+ return this.name;
3364
+ }
3365
+ /**
3366
+ * Formats key arguments for use in dictGet/dictHas SQL functions.
3367
+ * Strings are treated as SQL identifiers, numbers as literals.
3368
+ */
3369
+ formatKeyArgs(keys) {
3370
+ if (keys.length !== this.config.primaryKey.length) {
3371
+ throw new Error(
3372
+ `OlapDictionary '${this.name}': expected ${this.config.primaryKey.length} key argument(s) but got ${keys.length}.`
3373
+ );
3374
+ }
3375
+ const parts = keys.map((k) => {
3376
+ if (typeof k === "object" && "strings" in k) {
3377
+ return toStaticQuery(k);
3378
+ }
3379
+ if (typeof k === "string") {
3380
+ return `\`${k.replace(/`/g, "``")}\``;
3381
+ }
3382
+ return String(k);
3383
+ });
3384
+ return parts.length === 1 ? parts[0] : `(${parts.join(", ")})`;
3385
+ }
3386
+ /**
3387
+ * Generates a `dictGet('dict', 'attr', key)` SQL fragment.
3388
+ *
3389
+ * @param attr - The attribute (column) name to retrieve
3390
+ * @param keys - Key expression(s). Strings are treated as column identifiers.
3391
+ *
3392
+ * @example
3393
+ * ```typescript
3394
+ * sql`SELECT ${ProductDict.get("ProductName", "product_id")} AS name FROM ...`
3395
+ * // → SELECT dictGet('db.dict_products', 'ProductName', `product_id`) AS name FROM ...
3396
+ * ```
3397
+ */
3398
+ get(attr, ...keys) {
3399
+ if (!keys.length) {
3400
+ throw new Error(
3401
+ `OlapDictionary.get('${attr}'): at least one key argument is required.`
3402
+ );
3403
+ }
3404
+ const qualifiedName = this.getQualifiedName().replace(/'/g, "''");
3405
+ const escapedAttr = attr.replace(/'/g, "''");
3406
+ const keyExpr = this.formatKeyArgs(keys);
3407
+ return sql.raw(`dictGet('${qualifiedName}', '${escapedAttr}', ${keyExpr})`);
3408
+ }
3409
+ /**
3410
+ * Generates a `dictGetOrDefault('dict', 'attr', key, default)` SQL fragment.
3411
+ *
3412
+ * @param attr - The attribute (column) name to retrieve
3413
+ * @param defaultVal - The default value if the key is not found
3414
+ * @param keys - Key expression(s)
3415
+ */
3416
+ getOrDefault(attr, defaultVal, ...keys) {
3417
+ if (!keys.length) {
3418
+ throw new Error(
3419
+ `OlapDictionary.getOrDefault('${attr}'): at least one key argument is required.`
3420
+ );
3421
+ }
3422
+ const qualifiedName = this.getQualifiedName().replace(/'/g, "''");
3423
+ const escapedAttr = attr.replace(/'/g, "''");
3424
+ const keyExpr = this.formatKeyArgs(keys);
3425
+ let defaultExpr;
3426
+ if (typeof defaultVal === "object" && "strings" in defaultVal) {
3427
+ defaultExpr = toStaticQuery(defaultVal);
3428
+ } else if (typeof defaultVal === "string") {
3429
+ defaultExpr = `'${defaultVal.replace(/'/g, "''")}'`;
3430
+ } else {
3431
+ defaultExpr = String(defaultVal);
3432
+ }
3433
+ return sql.raw(
3434
+ `dictGetOrDefault('${qualifiedName}', '${escapedAttr}', ${keyExpr}, ${defaultExpr})`
3435
+ );
3436
+ }
3437
+ /**
3438
+ * Generates a `dictHas('dict', key)` SQL fragment.
3439
+ *
3440
+ * @param keys - Key expression(s)
3441
+ *
3442
+ * @example
3443
+ * ```typescript
3444
+ * sql`SELECT * FROM source WHERE ${ProductDict.has("product_id")}`
3445
+ * // → SELECT * FROM source WHERE dictHas('db.dict_products', `product_id`)
3446
+ * ```
3447
+ */
3448
+ has(...keys) {
3449
+ if (!keys.length) {
3450
+ throw new Error(
3451
+ `OlapDictionary.has(): at least one key argument is required.`
3452
+ );
3453
+ }
3454
+ const qualifiedName = this.getQualifiedName().replace(/'/g, "''");
3455
+ const keyExpr = this.formatKeyArgs(keys);
3456
+ return sql.raw(`dictHas('${qualifiedName}', ${keyExpr})`);
3457
+ }
3458
+ /**
3459
+ * Serializes this dictionary to the JSON format expected by the Rust CLI.
3460
+ * @internal
3461
+ */
3462
+ toJson() {
3463
+ const source = serializeSource(this.config);
3464
+ const result = {
3465
+ name: this.name,
3466
+ source,
3467
+ primaryKey: this.config.primaryKey,
3468
+ columns: this.serializedColumns,
3469
+ layout: serializeLayout(this.config.layout),
3470
+ lifetime: serializeLifetime(this.config.lifetime),
3471
+ settings: this.config.settings ?? {},
3472
+ lifeCycle: this.config.lifeCycle ?? "FULLY_MANAGED" /* FULLY_MANAGED */
3473
+ };
3474
+ if (this.config.database !== void 0)
3475
+ result.database = this.config.database;
3476
+ if (this.config.clusterName !== void 0)
3477
+ result.clusterName = this.config.clusterName;
3478
+ if (this.config.invalidateQuery !== void 0)
3479
+ result.invalidateQuery = this.config.invalidateQuery;
3480
+ if (this.config.comment !== void 0) result.comment = this.config.comment;
3481
+ if (this.config.metadata !== void 0)
3482
+ result.metadata = this.config.metadata;
3483
+ return result;
3484
+ }
3485
+ };
3486
+
3104
3487
  // src/dmv2/sdk/webApp.ts
3105
3488
  var RESERVED_MOUNT_PATHS = [
3106
3489
  "/admin",
@@ -3229,6 +3612,7 @@ Examples:
3229
3612
  };
3230
3613
  export {
3231
3614
  Api,
3615
+ COMPLEX_KEY_LAYOUTS,
3232
3616
  ClickHouseEngines,
3233
3617
  ConsumptionApi,
3234
3618
  DeadLetterQueue,
@@ -3237,6 +3621,7 @@ export {
3237
3621
  IngestPipeline,
3238
3622
  LifeCycle,
3239
3623
  MaterializedView,
3624
+ OlapDictionary,
3240
3625
  OlapTable,
3241
3626
  SelectRowPolicy,
3242
3627
  Sql,
@@ -3253,6 +3638,8 @@ export {
3253
3638
  getIngestApis,
3254
3639
  getMaterializedView,
3255
3640
  getMaterializedViews,
3641
+ getOlapDictionaries,
3642
+ getOlapDictionary,
3256
3643
  getSelectRowPolicies,
3257
3644
  getSelectRowPolicy,
3258
3645
  getSqlResource,