@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
@@ -633,6 +633,7 @@ var quoteIdentifier = (name) => {
633
633
  };
634
634
  var isTable = (value) => typeof value === "object" && value !== null && "kind" in value && value.kind === "OlapTable";
635
635
  var isView = (value) => typeof value === "object" && value !== null && "kind" in value && value.kind === "View";
636
+ var isDictionary = (value) => typeof value === "object" && value !== null && "kind" in value && value.kind === "OlapDictionary";
636
637
  var isColumn = (value) => typeof value === "object" && value !== null && !("kind" in value) && "name" in value && "annotations" in value;
637
638
  function sqlImpl(strings, ...values) {
638
639
  return new Sql(strings, values);
@@ -659,7 +660,7 @@ var Sql = class _Sql {
659
660
  );
660
661
  }
661
662
  const valuesLength = rawValues.reduce(
662
- (len, value) => len + (instanceofSql(value) ? value.values.length : isColumn(value) || isTable(value) || isView(value) ? 0 : 1),
663
+ (len, value) => len + (instanceofSql(value) ? value.values.length : isColumn(value) || isTable(value) || isView(value) || isDictionary(value) ? 0 : 1),
663
664
  0
664
665
  );
665
666
  this.values = new Array(valuesLength);
@@ -706,6 +707,14 @@ var Sql = class _Sql {
706
707
  this.strings[pos] += `\`${child.name}\``;
707
708
  }
708
709
  this.strings[pos] += rawString;
710
+ } else if (isDictionary(child)) {
711
+ if (/\b(?:FROM|JOIN)\s*$/i.test(this.strings[pos])) {
712
+ console.warn(
713
+ `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.`
714
+ );
715
+ }
716
+ this.strings[pos] += `'${child.getQualifiedName().replace(/'/g, "''")}'`;
717
+ this.strings[pos] += rawString;
709
718
  } else {
710
719
  this.values[pos++] = child;
711
720
  this.strings[pos] = rawString;
@@ -949,6 +958,12 @@ function getSelectRowPolicies() {
949
958
  function getSelectRowPolicy(name) {
950
959
  return getMooseInternal().selectRowPolicies.get(name);
951
960
  }
961
+ function getOlapDictionaries() {
962
+ return getMooseInternal().olapDictionaries;
963
+ }
964
+ function getOlapDictionary(name) {
965
+ return getMooseInternal().olapDictionaries.get(name);
966
+ }
952
967
 
953
968
  // src/consumption-apis/standalone.ts
954
969
  import { AsyncLocalStorage } from "async_hooks";
@@ -1146,7 +1161,8 @@ function createRegistryFrom(existing) {
1146
1161
  webApps: toTrackingMap(existing?.webApps),
1147
1162
  materializedViews: toTrackingMap(existing?.materializedViews),
1148
1163
  views: toTrackingMap(existing?.views),
1149
- selectRowPolicies: toTrackingMap(existing?.selectRowPolicies)
1164
+ selectRowPolicies: toTrackingMap(existing?.selectRowPolicies),
1165
+ olapDictionaries: toTrackingMap(existing?.olapDictionaries)
1150
1166
  };
1151
1167
  }
1152
1168
  var moose_internal = {
@@ -1186,6 +1202,10 @@ var moose_internal = {
1186
1202
  selectRowPolicies: new MutationTrackingMap(
1187
1203
  void 0,
1188
1204
  markRegistryMutated
1205
+ ),
1206
+ olapDictionaries: new MutationTrackingMap(
1207
+ void 0,
1208
+ markRegistryMutated
1189
1209
  )
1190
1210
  };
1191
1211
  var defaultRetentionPeriod = 60 * 60 * 24 * 7;
@@ -3067,6 +3087,369 @@ var LifeCycle = /* @__PURE__ */ ((LifeCycle2) => {
3067
3087
  return LifeCycle2;
3068
3088
  })(LifeCycle || {});
3069
3089
 
3090
+ // src/dmv2/sdk/olapDictionary.ts
3091
+ var COMPLEX_KEY_LAYOUTS = /* @__PURE__ */ new Set([
3092
+ "COMPLEX_KEY_HASHED",
3093
+ "COMPLEX_KEY_SPARSE_HASHED",
3094
+ "COMPLEX_KEY_HASHED_ARRAY",
3095
+ "COMPLEX_KEY_CACHE",
3096
+ "COMPLEX_KEY_SSD_CACHE",
3097
+ "COMPLEX_KEY_DIRECT"
3098
+ ]);
3099
+ function dataTypeToString(dataType) {
3100
+ if (typeof dataType === "string") {
3101
+ return dataType;
3102
+ }
3103
+ if (typeof dataType === "object" && dataType !== null) {
3104
+ if ("nullable" in dataType) {
3105
+ const inner = dataTypeToString(
3106
+ dataType.nullable
3107
+ );
3108
+ return `Nullable(${inner})`;
3109
+ }
3110
+ if ("elementType" in dataType) {
3111
+ const arr = dataType;
3112
+ const inner = dataTypeToString(arr.elementType);
3113
+ return arr.elementNullable ? `Array(Nullable(${inner}))` : `Array(${inner})`;
3114
+ }
3115
+ if ("name" in dataType && "values" in dataType) {
3116
+ const d = dataType;
3117
+ const entries = d.values.map(
3118
+ (v) => v.value.String !== void 0 ? `'${v.name}' = '${v.value.String}'` : `'${v.name}' = ${v.value.Int ?? 0}`
3119
+ ).join(", ");
3120
+ return `${d.name}(${entries})`;
3121
+ }
3122
+ }
3123
+ throw new Error(
3124
+ `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).`
3125
+ );
3126
+ }
3127
+ function buildDictionaryColumns(columns, overrides) {
3128
+ return columns.map((col) => {
3129
+ const override = overrides?.[col.name] ?? {};
3130
+ const result = {
3131
+ name: col.name,
3132
+ typeString: dataTypeToString(col.data_type)
3133
+ };
3134
+ if (override.defaultValue !== void 0)
3135
+ result.defaultValue = override.defaultValue;
3136
+ if (override.expression !== void 0)
3137
+ result.expression = override.expression;
3138
+ if (override.isInjective !== void 0)
3139
+ result.isInjective = override.isInjective;
3140
+ if (override.isHierarchical !== void 0)
3141
+ result.isHierarchical = override.isHierarchical;
3142
+ if (override.isObjectId !== void 0)
3143
+ result.isObjectId = override.isObjectId;
3144
+ if (override.comment !== void 0) result.comment = override.comment;
3145
+ return result;
3146
+ });
3147
+ }
3148
+ function serializeLifetime(lifetime) {
3149
+ if (typeof lifetime === "number") {
3150
+ if (!Number.isFinite(lifetime) || lifetime < 0 || !Number.isInteger(lifetime)) {
3151
+ throw new Error(
3152
+ `OlapDictionary: lifetime must be a finite non-negative integer (got ${lifetime}).`
3153
+ );
3154
+ }
3155
+ if (lifetime === 0) {
3156
+ return { type: "STATIC" };
3157
+ }
3158
+ return { type: "SINGLE", seconds: lifetime };
3159
+ }
3160
+ if (!Number.isFinite(lifetime.min) || !Number.isFinite(lifetime.max) || !Number.isInteger(lifetime.min) || !Number.isInteger(lifetime.max) || lifetime.min < 0 || lifetime.max < lifetime.min) {
3161
+ throw new Error(
3162
+ `OlapDictionary: lifetime range must use finite non-negative integers with min <= max (got min=${lifetime.min}, max=${lifetime.max}).`
3163
+ );
3164
+ }
3165
+ if (lifetime.min === 0 && lifetime.max === 0) {
3166
+ return { type: "STATIC" };
3167
+ }
3168
+ if (lifetime.min === lifetime.max) {
3169
+ return { type: "SINGLE", seconds: lifetime.min };
3170
+ }
3171
+ return { type: "RANGE", min: lifetime.min, max: lifetime.max };
3172
+ }
3173
+ function serializeLayout(layout) {
3174
+ const { type, ...rest } = layout;
3175
+ const snakeCaseFields = {};
3176
+ for (const [key, value] of Object.entries(rest)) {
3177
+ if (value === void 0) continue;
3178
+ const snake = key.replace(/([A-Z])/g, "_$1").toLowerCase();
3179
+ snakeCaseFields[snake] = value;
3180
+ }
3181
+ return { type, ...snakeCaseFields };
3182
+ }
3183
+ function externalTypeToRust(type) {
3184
+ const mapping = {
3185
+ http: "HTTP",
3186
+ clickhouse: "CLICK_HOUSE",
3187
+ mysql: "MYSQL",
3188
+ postgresql: "POSTGRESQL",
3189
+ redis: "REDIS",
3190
+ mongodb: "MONGODB",
3191
+ executable: "EXECUTABLE",
3192
+ s3: "S3"
3193
+ };
3194
+ return mapping[type];
3195
+ }
3196
+ function serializeExternalSource(ext) {
3197
+ const { type, ...rest } = ext;
3198
+ const inner = {
3199
+ source_type: externalTypeToRust(type)
3200
+ };
3201
+ for (const [key, value] of Object.entries(rest)) {
3202
+ inner[key] = value;
3203
+ }
3204
+ return {
3205
+ type: "EXTERNAL",
3206
+ externalSource: inner
3207
+ };
3208
+ }
3209
+ function serializeSource(config) {
3210
+ if (config.sourceTable !== void 0) {
3211
+ const table = config.sourceTable;
3212
+ if (table instanceof OlapTable) {
3213
+ return {
3214
+ type: "TABLE",
3215
+ table: table.generateTableName(),
3216
+ database: table.config.database,
3217
+ ...config.invalidateQuery !== void 0 ? { invalidateQuery: config.invalidateQuery } : {}
3218
+ };
3219
+ } else {
3220
+ return {
3221
+ type: "TABLE",
3222
+ table: table.name,
3223
+ database: table.database,
3224
+ ...config.invalidateQuery !== void 0 ? { invalidateQuery: config.invalidateQuery } : {}
3225
+ };
3226
+ }
3227
+ }
3228
+ if (config.sourceQuery !== void 0) {
3229
+ return {
3230
+ type: "QUERY",
3231
+ query: toStaticQuery(config.sourceQuery),
3232
+ ...config.invalidateQuery !== void 0 ? { invalidateQuery: config.invalidateQuery } : {}
3233
+ };
3234
+ }
3235
+ if (config.externalSource !== void 0) {
3236
+ return serializeExternalSource(config.externalSource);
3237
+ }
3238
+ throw new Error(
3239
+ "OlapDictionary: no source configured (unreachable after validation)"
3240
+ );
3241
+ }
3242
+ var OlapDictionary = class {
3243
+ /** @internal */
3244
+ kind = "OlapDictionary";
3245
+ /** Dictionary name */
3246
+ name;
3247
+ /** User configuration */
3248
+ config;
3249
+ /** Compiler-injected columns (name + type from T) */
3250
+ _columns;
3251
+ /** Serialized column list (DictionaryColumn JSON objects) */
3252
+ serializedColumns;
3253
+ constructor(name, config, _schema, columns) {
3254
+ if (_schema === void 0 || columns === void 0) {
3255
+ throw new Error(
3256
+ "Supply the type param T so that the schema is inserted by the compiler plugin."
3257
+ );
3258
+ }
3259
+ this.name = name;
3260
+ this.config = config;
3261
+ this._columns = columns;
3262
+ const sourcesSet = [
3263
+ config.sourceTable !== void 0,
3264
+ config.sourceQuery !== void 0,
3265
+ config.externalSource !== void 0
3266
+ ].filter(Boolean).length;
3267
+ if (sourcesSet === 0) {
3268
+ throw new Error(
3269
+ `OlapDictionary '${name}': exactly one of sourceTable, sourceQuery, or externalSource must be set (none provided).`
3270
+ );
3271
+ }
3272
+ if (sourcesSet > 1) {
3273
+ throw new Error(
3274
+ `OlapDictionary '${name}': exactly one of sourceTable, sourceQuery, or externalSource must be set (${sourcesSet} provided).`
3275
+ );
3276
+ }
3277
+ if (config.sourceQuery !== void 0 && !config.sourceTables?.length) {
3278
+ throw new Error(
3279
+ `OlapDictionary '${name}': sourceQuery requires sourceTables to be set for dependency tracking.`
3280
+ );
3281
+ }
3282
+ if (config.sourceQuery !== void 0) {
3283
+ if (!toStaticQuery(config.sourceQuery).trim()) {
3284
+ throw new Error(
3285
+ `OlapDictionary '${name}': sourceQuery must not be blank.`
3286
+ );
3287
+ }
3288
+ }
3289
+ if (!config.primaryKey.length) {
3290
+ throw new Error(
3291
+ `OlapDictionary '${name}': primaryKey must contain at least one column name.`
3292
+ );
3293
+ }
3294
+ if (!COMPLEX_KEY_LAYOUTS.has(config.layout.type)) {
3295
+ if (config.primaryKey.length !== 1) {
3296
+ throw new Error(
3297
+ `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.`
3298
+ );
3299
+ }
3300
+ }
3301
+ this.serializedColumns = buildDictionaryColumns(
3302
+ columns,
3303
+ config.columns
3304
+ );
3305
+ if (!config.metadata?.source) {
3306
+ const stack = new Error().stack;
3307
+ const sourceInfo = getSourceFileFromStack(stack);
3308
+ if (sourceInfo) {
3309
+ this.config.metadata = {
3310
+ ...config.metadata,
3311
+ source: { file: sourceInfo }
3312
+ };
3313
+ }
3314
+ }
3315
+ const olapDictionaries = getMooseInternal().olapDictionaries;
3316
+ if (!isClientOnlyMode() && olapDictionaries.has(name)) {
3317
+ throw new Error(`OlapDictionary with name '${name}' already exists`);
3318
+ }
3319
+ olapDictionaries.set(name, this);
3320
+ }
3321
+ /**
3322
+ * Returns the qualified dictionary name for use in dictGet calls.
3323
+ * Format: `database.name` if database is set, otherwise just `name`.
3324
+ */
3325
+ getQualifiedName() {
3326
+ if (this.config.database) {
3327
+ return `${this.config.database}.${this.name}`;
3328
+ }
3329
+ return this.name;
3330
+ }
3331
+ /**
3332
+ * Formats key arguments for use in dictGet/dictHas SQL functions.
3333
+ * Strings are treated as SQL identifiers, numbers as literals.
3334
+ */
3335
+ formatKeyArgs(keys) {
3336
+ if (keys.length !== this.config.primaryKey.length) {
3337
+ throw new Error(
3338
+ `OlapDictionary '${this.name}': expected ${this.config.primaryKey.length} key argument(s) but got ${keys.length}.`
3339
+ );
3340
+ }
3341
+ const parts = keys.map((k) => {
3342
+ if (typeof k === "object" && "strings" in k) {
3343
+ return toStaticQuery(k);
3344
+ }
3345
+ if (typeof k === "string") {
3346
+ return `\`${k.replace(/`/g, "``")}\``;
3347
+ }
3348
+ return String(k);
3349
+ });
3350
+ return parts.length === 1 ? parts[0] : `(${parts.join(", ")})`;
3351
+ }
3352
+ /**
3353
+ * Generates a `dictGet('dict', 'attr', key)` SQL fragment.
3354
+ *
3355
+ * @param attr - The attribute (column) name to retrieve
3356
+ * @param keys - Key expression(s). Strings are treated as column identifiers.
3357
+ *
3358
+ * @example
3359
+ * ```typescript
3360
+ * sql`SELECT ${ProductDict.get("ProductName", "product_id")} AS name FROM ...`
3361
+ * // → SELECT dictGet('db.dict_products', 'ProductName', `product_id`) AS name FROM ...
3362
+ * ```
3363
+ */
3364
+ get(attr, ...keys) {
3365
+ if (!keys.length) {
3366
+ throw new Error(
3367
+ `OlapDictionary.get('${attr}'): at least one key argument is required.`
3368
+ );
3369
+ }
3370
+ const qualifiedName = this.getQualifiedName().replace(/'/g, "''");
3371
+ const escapedAttr = attr.replace(/'/g, "''");
3372
+ const keyExpr = this.formatKeyArgs(keys);
3373
+ return sql.raw(`dictGet('${qualifiedName}', '${escapedAttr}', ${keyExpr})`);
3374
+ }
3375
+ /**
3376
+ * Generates a `dictGetOrDefault('dict', 'attr', key, default)` SQL fragment.
3377
+ *
3378
+ * @param attr - The attribute (column) name to retrieve
3379
+ * @param defaultVal - The default value if the key is not found
3380
+ * @param keys - Key expression(s)
3381
+ */
3382
+ getOrDefault(attr, defaultVal, ...keys) {
3383
+ if (!keys.length) {
3384
+ throw new Error(
3385
+ `OlapDictionary.getOrDefault('${attr}'): at least one key argument is required.`
3386
+ );
3387
+ }
3388
+ const qualifiedName = this.getQualifiedName().replace(/'/g, "''");
3389
+ const escapedAttr = attr.replace(/'/g, "''");
3390
+ const keyExpr = this.formatKeyArgs(keys);
3391
+ let defaultExpr;
3392
+ if (typeof defaultVal === "object" && "strings" in defaultVal) {
3393
+ defaultExpr = toStaticQuery(defaultVal);
3394
+ } else if (typeof defaultVal === "string") {
3395
+ defaultExpr = `'${defaultVal.replace(/'/g, "''")}'`;
3396
+ } else {
3397
+ defaultExpr = String(defaultVal);
3398
+ }
3399
+ return sql.raw(
3400
+ `dictGetOrDefault('${qualifiedName}', '${escapedAttr}', ${keyExpr}, ${defaultExpr})`
3401
+ );
3402
+ }
3403
+ /**
3404
+ * Generates a `dictHas('dict', key)` SQL fragment.
3405
+ *
3406
+ * @param keys - Key expression(s)
3407
+ *
3408
+ * @example
3409
+ * ```typescript
3410
+ * sql`SELECT * FROM source WHERE ${ProductDict.has("product_id")}`
3411
+ * // → SELECT * FROM source WHERE dictHas('db.dict_products', `product_id`)
3412
+ * ```
3413
+ */
3414
+ has(...keys) {
3415
+ if (!keys.length) {
3416
+ throw new Error(
3417
+ `OlapDictionary.has(): at least one key argument is required.`
3418
+ );
3419
+ }
3420
+ const qualifiedName = this.getQualifiedName().replace(/'/g, "''");
3421
+ const keyExpr = this.formatKeyArgs(keys);
3422
+ return sql.raw(`dictHas('${qualifiedName}', ${keyExpr})`);
3423
+ }
3424
+ /**
3425
+ * Serializes this dictionary to the JSON format expected by the Rust CLI.
3426
+ * @internal
3427
+ */
3428
+ toJson() {
3429
+ const source = serializeSource(this.config);
3430
+ const result = {
3431
+ name: this.name,
3432
+ source,
3433
+ primaryKey: this.config.primaryKey,
3434
+ columns: this.serializedColumns,
3435
+ layout: serializeLayout(this.config.layout),
3436
+ lifetime: serializeLifetime(this.config.lifetime),
3437
+ settings: this.config.settings ?? {},
3438
+ lifeCycle: this.config.lifeCycle ?? "FULLY_MANAGED" /* FULLY_MANAGED */
3439
+ };
3440
+ if (this.config.database !== void 0)
3441
+ result.database = this.config.database;
3442
+ if (this.config.clusterName !== void 0)
3443
+ result.clusterName = this.config.clusterName;
3444
+ if (this.config.invalidateQuery !== void 0)
3445
+ result.invalidateQuery = this.config.invalidateQuery;
3446
+ if (this.config.comment !== void 0) result.comment = this.config.comment;
3447
+ if (this.config.metadata !== void 0)
3448
+ result.metadata = this.config.metadata;
3449
+ return result;
3450
+ }
3451
+ };
3452
+
3070
3453
  // src/dmv2/sdk/webApp.ts
3071
3454
  var RESERVED_MOUNT_PATHS = [
3072
3455
  "/admin",
@@ -3195,6 +3578,7 @@ Examples:
3195
3578
  };
3196
3579
  export {
3197
3580
  Api,
3581
+ COMPLEX_KEY_LAYOUTS,
3198
3582
  ClickHouseEngines,
3199
3583
  ConsumptionApi,
3200
3584
  DeadLetterQueue,
@@ -3203,6 +3587,7 @@ export {
3203
3587
  IngestPipeline,
3204
3588
  LifeCycle,
3205
3589
  MaterializedView,
3590
+ OlapDictionary,
3206
3591
  OlapTable,
3207
3592
  SelectRowPolicy,
3208
3593
  SqlResource,
@@ -3217,6 +3602,8 @@ export {
3217
3602
  getIngestApis,
3218
3603
  getMaterializedView,
3219
3604
  getMaterializedViews,
3605
+ getOlapDictionaries,
3606
+ getOlapDictionary,
3220
3607
  getSelectRowPolicies,
3221
3608
  getSelectRowPolicy,
3222
3609
  getSqlResource,