@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
package/dist/index.mjs CHANGED
@@ -635,6 +635,7 @@ var quoteIdentifier = (name) => {
635
635
  };
636
636
  var isTable = (value) => typeof value === "object" && value !== null && "kind" in value && value.kind === "OlapTable";
637
637
  var isView = (value) => typeof value === "object" && value !== null && "kind" in value && value.kind === "View";
638
+ var isDictionary = (value) => typeof value === "object" && value !== null && "kind" in value && value.kind === "OlapDictionary";
638
639
  var isColumn = (value) => typeof value === "object" && value !== null && !("kind" in value) && "name" in value && "annotations" in value;
639
640
  function sqlImpl(strings, ...values) {
640
641
  return new Sql(strings, values);
@@ -661,7 +662,7 @@ var Sql = class _Sql {
661
662
  );
662
663
  }
663
664
  const valuesLength = rawValues.reduce(
664
- (len, value) => len + (instanceofSql(value) ? value.values.length : isColumn(value) || isTable(value) || isView(value) ? 0 : 1),
665
+ (len, value) => len + (instanceofSql(value) ? value.values.length : isColumn(value) || isTable(value) || isView(value) || isDictionary(value) ? 0 : 1),
665
666
  0
666
667
  );
667
668
  this.values = new Array(valuesLength);
@@ -708,6 +709,14 @@ var Sql = class _Sql {
708
709
  this.strings[pos] += `\`${child.name}\``;
709
710
  }
710
711
  this.strings[pos] += rawString;
712
+ } else if (isDictionary(child)) {
713
+ if (/\b(?:FROM|JOIN)\s*$/i.test(this.strings[pos])) {
714
+ console.warn(
715
+ `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.`
716
+ );
717
+ }
718
+ this.strings[pos] += `'${child.getQualifiedName().replace(/'/g, "''")}'`;
719
+ this.strings[pos] += rawString;
711
720
  } else {
712
721
  this.values[pos++] = child;
713
722
  this.strings[pos] = rawString;
@@ -1049,7 +1058,8 @@ function createRegistryFrom(existing) {
1049
1058
  webApps: toTrackingMap(existing?.webApps),
1050
1059
  materializedViews: toTrackingMap(existing?.materializedViews),
1051
1060
  views: toTrackingMap(existing?.views),
1052
- selectRowPolicies: toTrackingMap(existing?.selectRowPolicies)
1061
+ selectRowPolicies: toTrackingMap(existing?.selectRowPolicies),
1062
+ olapDictionaries: toTrackingMap(existing?.olapDictionaries)
1053
1063
  };
1054
1064
  }
1055
1065
  var moose_internal = {
@@ -1089,6 +1099,10 @@ var moose_internal = {
1089
1099
  selectRowPolicies: new MutationTrackingMap(
1090
1100
  void 0,
1091
1101
  markRegistryMutated
1102
+ ),
1103
+ olapDictionaries: new MutationTrackingMap(
1104
+ void 0,
1105
+ markRegistryMutated
1092
1106
  )
1093
1107
  };
1094
1108
  var defaultRetentionPeriod = 60 * 60 * 24 * 7;
@@ -1121,6 +1135,7 @@ var loadIndex = async () => {
1121
1135
  registry.materializedViews.clear();
1122
1136
  registry.views.clear();
1123
1137
  registry.selectRowPolicies.clear();
1138
+ registry.olapDictionaries.clear();
1124
1139
  const outDir = getOutDir();
1125
1140
  const compiledDir = path4.isAbsolute(outDir) ? outDir : path4.join(process3.cwd(), outDir);
1126
1141
  Object.keys(__require.cache).forEach((key) => {
@@ -3023,6 +3038,369 @@ var LifeCycle = /* @__PURE__ */ ((LifeCycle2) => {
3023
3038
  return LifeCycle2;
3024
3039
  })(LifeCycle || {});
3025
3040
 
3041
+ // src/dmv2/sdk/olapDictionary.ts
3042
+ var COMPLEX_KEY_LAYOUTS = /* @__PURE__ */ new Set([
3043
+ "COMPLEX_KEY_HASHED",
3044
+ "COMPLEX_KEY_SPARSE_HASHED",
3045
+ "COMPLEX_KEY_HASHED_ARRAY",
3046
+ "COMPLEX_KEY_CACHE",
3047
+ "COMPLEX_KEY_SSD_CACHE",
3048
+ "COMPLEX_KEY_DIRECT"
3049
+ ]);
3050
+ function dataTypeToString(dataType) {
3051
+ if (typeof dataType === "string") {
3052
+ return dataType;
3053
+ }
3054
+ if (typeof dataType === "object" && dataType !== null) {
3055
+ if ("nullable" in dataType) {
3056
+ const inner = dataTypeToString(
3057
+ dataType.nullable
3058
+ );
3059
+ return `Nullable(${inner})`;
3060
+ }
3061
+ if ("elementType" in dataType) {
3062
+ const arr = dataType;
3063
+ const inner = dataTypeToString(arr.elementType);
3064
+ return arr.elementNullable ? `Array(Nullable(${inner}))` : `Array(${inner})`;
3065
+ }
3066
+ if ("name" in dataType && "values" in dataType) {
3067
+ const d = dataType;
3068
+ const entries = d.values.map(
3069
+ (v) => v.value.String !== void 0 ? `'${v.name}' = '${v.value.String}'` : `'${v.name}' = ${v.value.Int ?? 0}`
3070
+ ).join(", ");
3071
+ return `${d.name}(${entries})`;
3072
+ }
3073
+ }
3074
+ throw new Error(
3075
+ `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).`
3076
+ );
3077
+ }
3078
+ function buildDictionaryColumns(columns, overrides) {
3079
+ return columns.map((col) => {
3080
+ const override = overrides?.[col.name] ?? {};
3081
+ const result = {
3082
+ name: col.name,
3083
+ typeString: dataTypeToString(col.data_type)
3084
+ };
3085
+ if (override.defaultValue !== void 0)
3086
+ result.defaultValue = override.defaultValue;
3087
+ if (override.expression !== void 0)
3088
+ result.expression = override.expression;
3089
+ if (override.isInjective !== void 0)
3090
+ result.isInjective = override.isInjective;
3091
+ if (override.isHierarchical !== void 0)
3092
+ result.isHierarchical = override.isHierarchical;
3093
+ if (override.isObjectId !== void 0)
3094
+ result.isObjectId = override.isObjectId;
3095
+ if (override.comment !== void 0) result.comment = override.comment;
3096
+ return result;
3097
+ });
3098
+ }
3099
+ function serializeLifetime(lifetime) {
3100
+ if (typeof lifetime === "number") {
3101
+ if (!Number.isFinite(lifetime) || lifetime < 0 || !Number.isInteger(lifetime)) {
3102
+ throw new Error(
3103
+ `OlapDictionary: lifetime must be a finite non-negative integer (got ${lifetime}).`
3104
+ );
3105
+ }
3106
+ if (lifetime === 0) {
3107
+ return { type: "STATIC" };
3108
+ }
3109
+ return { type: "SINGLE", seconds: lifetime };
3110
+ }
3111
+ if (!Number.isFinite(lifetime.min) || !Number.isFinite(lifetime.max) || !Number.isInteger(lifetime.min) || !Number.isInteger(lifetime.max) || lifetime.min < 0 || lifetime.max < lifetime.min) {
3112
+ throw new Error(
3113
+ `OlapDictionary: lifetime range must use finite non-negative integers with min <= max (got min=${lifetime.min}, max=${lifetime.max}).`
3114
+ );
3115
+ }
3116
+ if (lifetime.min === 0 && lifetime.max === 0) {
3117
+ return { type: "STATIC" };
3118
+ }
3119
+ if (lifetime.min === lifetime.max) {
3120
+ return { type: "SINGLE", seconds: lifetime.min };
3121
+ }
3122
+ return { type: "RANGE", min: lifetime.min, max: lifetime.max };
3123
+ }
3124
+ function serializeLayout(layout) {
3125
+ const { type, ...rest } = layout;
3126
+ const snakeCaseFields = {};
3127
+ for (const [key, value] of Object.entries(rest)) {
3128
+ if (value === void 0) continue;
3129
+ const snake = key.replace(/([A-Z])/g, "_$1").toLowerCase();
3130
+ snakeCaseFields[snake] = value;
3131
+ }
3132
+ return { type, ...snakeCaseFields };
3133
+ }
3134
+ function externalTypeToRust(type) {
3135
+ const mapping = {
3136
+ http: "HTTP",
3137
+ clickhouse: "CLICK_HOUSE",
3138
+ mysql: "MYSQL",
3139
+ postgresql: "POSTGRESQL",
3140
+ redis: "REDIS",
3141
+ mongodb: "MONGODB",
3142
+ executable: "EXECUTABLE",
3143
+ s3: "S3"
3144
+ };
3145
+ return mapping[type];
3146
+ }
3147
+ function serializeExternalSource(ext) {
3148
+ const { type, ...rest } = ext;
3149
+ const inner = {
3150
+ source_type: externalTypeToRust(type)
3151
+ };
3152
+ for (const [key, value] of Object.entries(rest)) {
3153
+ inner[key] = value;
3154
+ }
3155
+ return {
3156
+ type: "EXTERNAL",
3157
+ externalSource: inner
3158
+ };
3159
+ }
3160
+ function serializeSource(config) {
3161
+ if (config.sourceTable !== void 0) {
3162
+ const table = config.sourceTable;
3163
+ if (table instanceof OlapTable) {
3164
+ return {
3165
+ type: "TABLE",
3166
+ table: table.generateTableName(),
3167
+ database: table.config.database,
3168
+ ...config.invalidateQuery !== void 0 ? { invalidateQuery: config.invalidateQuery } : {}
3169
+ };
3170
+ } else {
3171
+ return {
3172
+ type: "TABLE",
3173
+ table: table.name,
3174
+ database: table.database,
3175
+ ...config.invalidateQuery !== void 0 ? { invalidateQuery: config.invalidateQuery } : {}
3176
+ };
3177
+ }
3178
+ }
3179
+ if (config.sourceQuery !== void 0) {
3180
+ return {
3181
+ type: "QUERY",
3182
+ query: toStaticQuery(config.sourceQuery),
3183
+ ...config.invalidateQuery !== void 0 ? { invalidateQuery: config.invalidateQuery } : {}
3184
+ };
3185
+ }
3186
+ if (config.externalSource !== void 0) {
3187
+ return serializeExternalSource(config.externalSource);
3188
+ }
3189
+ throw new Error(
3190
+ "OlapDictionary: no source configured (unreachable after validation)"
3191
+ );
3192
+ }
3193
+ var OlapDictionary = class {
3194
+ /** @internal */
3195
+ kind = "OlapDictionary";
3196
+ /** Dictionary name */
3197
+ name;
3198
+ /** User configuration */
3199
+ config;
3200
+ /** Compiler-injected columns (name + type from T) */
3201
+ _columns;
3202
+ /** Serialized column list (DictionaryColumn JSON objects) */
3203
+ serializedColumns;
3204
+ constructor(name, config, _schema, columns) {
3205
+ if (_schema === void 0 || columns === void 0) {
3206
+ throw new Error(
3207
+ "Supply the type param T so that the schema is inserted by the compiler plugin."
3208
+ );
3209
+ }
3210
+ this.name = name;
3211
+ this.config = config;
3212
+ this._columns = columns;
3213
+ const sourcesSet = [
3214
+ config.sourceTable !== void 0,
3215
+ config.sourceQuery !== void 0,
3216
+ config.externalSource !== void 0
3217
+ ].filter(Boolean).length;
3218
+ if (sourcesSet === 0) {
3219
+ throw new Error(
3220
+ `OlapDictionary '${name}': exactly one of sourceTable, sourceQuery, or externalSource must be set (none provided).`
3221
+ );
3222
+ }
3223
+ if (sourcesSet > 1) {
3224
+ throw new Error(
3225
+ `OlapDictionary '${name}': exactly one of sourceTable, sourceQuery, or externalSource must be set (${sourcesSet} provided).`
3226
+ );
3227
+ }
3228
+ if (config.sourceQuery !== void 0 && !config.sourceTables?.length) {
3229
+ throw new Error(
3230
+ `OlapDictionary '${name}': sourceQuery requires sourceTables to be set for dependency tracking.`
3231
+ );
3232
+ }
3233
+ if (config.sourceQuery !== void 0) {
3234
+ if (!toStaticQuery(config.sourceQuery).trim()) {
3235
+ throw new Error(
3236
+ `OlapDictionary '${name}': sourceQuery must not be blank.`
3237
+ );
3238
+ }
3239
+ }
3240
+ if (!config.primaryKey.length) {
3241
+ throw new Error(
3242
+ `OlapDictionary '${name}': primaryKey must contain at least one column name.`
3243
+ );
3244
+ }
3245
+ if (!COMPLEX_KEY_LAYOUTS.has(config.layout.type)) {
3246
+ if (config.primaryKey.length !== 1) {
3247
+ throw new Error(
3248
+ `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.`
3249
+ );
3250
+ }
3251
+ }
3252
+ this.serializedColumns = buildDictionaryColumns(
3253
+ columns,
3254
+ config.columns
3255
+ );
3256
+ if (!config.metadata?.source) {
3257
+ const stack = new Error().stack;
3258
+ const sourceInfo = getSourceFileFromStack(stack);
3259
+ if (sourceInfo) {
3260
+ this.config.metadata = {
3261
+ ...config.metadata,
3262
+ source: { file: sourceInfo }
3263
+ };
3264
+ }
3265
+ }
3266
+ const olapDictionaries = getMooseInternal().olapDictionaries;
3267
+ if (!isClientOnlyMode() && olapDictionaries.has(name)) {
3268
+ throw new Error(`OlapDictionary with name '${name}' already exists`);
3269
+ }
3270
+ olapDictionaries.set(name, this);
3271
+ }
3272
+ /**
3273
+ * Returns the qualified dictionary name for use in dictGet calls.
3274
+ * Format: `database.name` if database is set, otherwise just `name`.
3275
+ */
3276
+ getQualifiedName() {
3277
+ if (this.config.database) {
3278
+ return `${this.config.database}.${this.name}`;
3279
+ }
3280
+ return this.name;
3281
+ }
3282
+ /**
3283
+ * Formats key arguments for use in dictGet/dictHas SQL functions.
3284
+ * Strings are treated as SQL identifiers, numbers as literals.
3285
+ */
3286
+ formatKeyArgs(keys) {
3287
+ if (keys.length !== this.config.primaryKey.length) {
3288
+ throw new Error(
3289
+ `OlapDictionary '${this.name}': expected ${this.config.primaryKey.length} key argument(s) but got ${keys.length}.`
3290
+ );
3291
+ }
3292
+ const parts = keys.map((k) => {
3293
+ if (typeof k === "object" && "strings" in k) {
3294
+ return toStaticQuery(k);
3295
+ }
3296
+ if (typeof k === "string") {
3297
+ return `\`${k.replace(/`/g, "``")}\``;
3298
+ }
3299
+ return String(k);
3300
+ });
3301
+ return parts.length === 1 ? parts[0] : `(${parts.join(", ")})`;
3302
+ }
3303
+ /**
3304
+ * Generates a `dictGet('dict', 'attr', key)` SQL fragment.
3305
+ *
3306
+ * @param attr - The attribute (column) name to retrieve
3307
+ * @param keys - Key expression(s). Strings are treated as column identifiers.
3308
+ *
3309
+ * @example
3310
+ * ```typescript
3311
+ * sql`SELECT ${ProductDict.get("ProductName", "product_id")} AS name FROM ...`
3312
+ * // → SELECT dictGet('db.dict_products', 'ProductName', `product_id`) AS name FROM ...
3313
+ * ```
3314
+ */
3315
+ get(attr, ...keys) {
3316
+ if (!keys.length) {
3317
+ throw new Error(
3318
+ `OlapDictionary.get('${attr}'): at least one key argument is required.`
3319
+ );
3320
+ }
3321
+ const qualifiedName = this.getQualifiedName().replace(/'/g, "''");
3322
+ const escapedAttr = attr.replace(/'/g, "''");
3323
+ const keyExpr = this.formatKeyArgs(keys);
3324
+ return sql.raw(`dictGet('${qualifiedName}', '${escapedAttr}', ${keyExpr})`);
3325
+ }
3326
+ /**
3327
+ * Generates a `dictGetOrDefault('dict', 'attr', key, default)` SQL fragment.
3328
+ *
3329
+ * @param attr - The attribute (column) name to retrieve
3330
+ * @param defaultVal - The default value if the key is not found
3331
+ * @param keys - Key expression(s)
3332
+ */
3333
+ getOrDefault(attr, defaultVal, ...keys) {
3334
+ if (!keys.length) {
3335
+ throw new Error(
3336
+ `OlapDictionary.getOrDefault('${attr}'): at least one key argument is required.`
3337
+ );
3338
+ }
3339
+ const qualifiedName = this.getQualifiedName().replace(/'/g, "''");
3340
+ const escapedAttr = attr.replace(/'/g, "''");
3341
+ const keyExpr = this.formatKeyArgs(keys);
3342
+ let defaultExpr;
3343
+ if (typeof defaultVal === "object" && "strings" in defaultVal) {
3344
+ defaultExpr = toStaticQuery(defaultVal);
3345
+ } else if (typeof defaultVal === "string") {
3346
+ defaultExpr = `'${defaultVal.replace(/'/g, "''")}'`;
3347
+ } else {
3348
+ defaultExpr = String(defaultVal);
3349
+ }
3350
+ return sql.raw(
3351
+ `dictGetOrDefault('${qualifiedName}', '${escapedAttr}', ${keyExpr}, ${defaultExpr})`
3352
+ );
3353
+ }
3354
+ /**
3355
+ * Generates a `dictHas('dict', key)` SQL fragment.
3356
+ *
3357
+ * @param keys - Key expression(s)
3358
+ *
3359
+ * @example
3360
+ * ```typescript
3361
+ * sql`SELECT * FROM source WHERE ${ProductDict.has("product_id")}`
3362
+ * // → SELECT * FROM source WHERE dictHas('db.dict_products', `product_id`)
3363
+ * ```
3364
+ */
3365
+ has(...keys) {
3366
+ if (!keys.length) {
3367
+ throw new Error(
3368
+ `OlapDictionary.has(): at least one key argument is required.`
3369
+ );
3370
+ }
3371
+ const qualifiedName = this.getQualifiedName().replace(/'/g, "''");
3372
+ const keyExpr = this.formatKeyArgs(keys);
3373
+ return sql.raw(`dictHas('${qualifiedName}', ${keyExpr})`);
3374
+ }
3375
+ /**
3376
+ * Serializes this dictionary to the JSON format expected by the Rust CLI.
3377
+ * @internal
3378
+ */
3379
+ toJson() {
3380
+ const source = serializeSource(this.config);
3381
+ const result = {
3382
+ name: this.name,
3383
+ source,
3384
+ primaryKey: this.config.primaryKey,
3385
+ columns: this.serializedColumns,
3386
+ layout: serializeLayout(this.config.layout),
3387
+ lifetime: serializeLifetime(this.config.lifetime),
3388
+ settings: this.config.settings ?? {},
3389
+ lifeCycle: this.config.lifeCycle ?? "FULLY_MANAGED" /* FULLY_MANAGED */
3390
+ };
3391
+ if (this.config.database !== void 0)
3392
+ result.database = this.config.database;
3393
+ if (this.config.clusterName !== void 0)
3394
+ result.clusterName = this.config.clusterName;
3395
+ if (this.config.invalidateQuery !== void 0)
3396
+ result.invalidateQuery = this.config.invalidateQuery;
3397
+ if (this.config.comment !== void 0) result.comment = this.config.comment;
3398
+ if (this.config.metadata !== void 0)
3399
+ result.metadata = this.config.metadata;
3400
+ return result;
3401
+ }
3402
+ };
3403
+
3026
3404
  // src/dmv2/sdk/webApp.ts
3027
3405
  var RESERVED_MOUNT_PATHS = [
3028
3406
  "/admin",
@@ -3233,6 +3611,12 @@ function getSelectRowPolicies() {
3233
3611
  function getSelectRowPolicy(name) {
3234
3612
  return getMooseInternal().selectRowPolicies.get(name);
3235
3613
  }
3614
+ function getOlapDictionaries() {
3615
+ return getMooseInternal().olapDictionaries;
3616
+ }
3617
+ function getOlapDictionary(name) {
3618
+ return getMooseInternal().olapDictionaries.get(name);
3619
+ }
3236
3620
 
3237
3621
  // src/index.ts
3238
3622
  init_commons();
@@ -5104,6 +5488,7 @@ export {
5104
5488
  Api,
5105
5489
  ApiHelpers,
5106
5490
  BadRequestError,
5491
+ COMPLEX_KEY_LAYOUTS,
5107
5492
  CSV_DELIMITERS,
5108
5493
  ClickHouseEngines,
5109
5494
  ConsumptionApi,
@@ -5126,6 +5511,7 @@ export {
5126
5511
  MaterializedView,
5127
5512
  MooseCache,
5128
5513
  MooseClient,
5514
+ OlapDictionary,
5129
5515
  OlapTable,
5130
5516
  QueryClient,
5131
5517
  RETRY_FACTOR_PRODUCER,
@@ -5178,6 +5564,8 @@ export {
5178
5564
  getMooseClients,
5179
5565
  getMooseUtils,
5180
5566
  getMooseUtilsFromRequest,
5567
+ getOlapDictionaries,
5568
+ getOlapDictionary,
5181
5569
  getSelectRowPolicies,
5182
5570
  getSelectRowPolicy,
5183
5571
  getSqlResource,