@deephaven-enterprise/query-utils 2026.1.54 → 2026.1.57

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.
@@ -11,6 +11,10 @@
11
11
  * @returns An array of tokens. Returns an empty array for empty/whitespace input.
12
12
  */
13
13
  export declare function tokenizeArgs(args: string): string[];
14
+ /** Removes blank list entries while preserving every nonblank value verbatim. */
15
+ export declare function removeBlankEntries(entries: readonly string[]): string[];
16
+ /** Removes fully blank name/value pairs while preserving nonblank pairs. */
17
+ export declare function removeBlankEnvVarEntries(entries: readonly string[]): string[];
14
18
  /**
15
19
  * Joins a flat alternating key/value array into a space-separated
16
20
  * `KEY=VALUE` string. Values are shell-quoted as needed so the output can be
@@ -19,7 +23,7 @@ export declare function tokenizeArgs(args: string): string[];
19
23
  * @param entries Flat alternating array: `["KEY1", "VALUE1", "KEY2", "VALUE2", …]`
20
24
  * @returns Shell-safe string: `"KEY1=VALUE1 KEY2='value with spaces'"`
21
25
  */
22
- export declare function joinEnvVarEntries(entries: string[]): string;
26
+ export declare function joinEnvVarEntries(entries: readonly string[]): string;
23
27
  /**
24
28
  * Parses a space-separated `KEY=VALUE` string into a flat alternating
25
29
  * key/value array. Uses shell quoting rules so that quoted spans are
@@ -33,13 +37,15 @@ export declare function joinEnvVarEntries(entries: string[]): string;
33
37
  * @throws {Error} If any token has an empty key or is a shell operator/glob.
34
38
  */
35
39
  export declare function splitEnvVarEntries(envVars: string): string[];
40
+ /** Returns an error for invalid environment variable entries, or null. */
41
+ export declare function getEnvVarEntriesValidationError(entries: readonly string[]): string | null;
36
42
  /**
37
43
  * Joins an array of classpath entries into a colon-separated string.
38
44
  *
39
45
  * @param entries Array of classpath entries, one path per element.
40
46
  * @returns Colon-separated string: `"path1:path2:path3"`
41
47
  */
42
- export declare function joinClasspathAdditions(entries: string[]): string;
48
+ export declare function joinClasspathAdditions(entries: readonly string[]): string;
43
49
  /**
44
50
  * Splits a colon-separated classpath string into an array of entries,
45
51
  * filtering out any empty segments.
@@ -15,6 +15,23 @@ export function tokenizeArgs(args) {
15
15
  var _a;
16
16
  return (_a = args.match(/(?:[^\s"']+|"[^"]*"|'[^']*'|["'])+/g)) !== null && _a !== void 0 ? _a : [];
17
17
  }
18
+ /** Removes blank list entries while preserving every nonblank value verbatim. */
19
+ export function removeBlankEntries(entries) {
20
+ return entries.filter(entry => entry.trim() !== '');
21
+ }
22
+ /** Removes fully blank name/value pairs while preserving nonblank pairs. */
23
+ export function removeBlankEnvVarEntries(entries) {
24
+ var _a, _b;
25
+ const result = [];
26
+ for (let index = 0; index < entries.length; index += 2) {
27
+ const name = (_a = entries[index]) !== null && _a !== void 0 ? _a : '';
28
+ const value = (_b = entries[index + 1]) !== null && _b !== void 0 ? _b : '';
29
+ if (name.trim() !== '' || value.trim() !== '') {
30
+ result.push(name, value);
31
+ }
32
+ }
33
+ return result;
34
+ }
18
35
  /**
19
36
  * Joins a flat alternating key/value array into a space-separated
20
37
  * `KEY=VALUE` string. Values are shell-quoted as needed so the output can be
@@ -28,8 +45,9 @@ export function joinEnvVarEntries(entries) {
28
45
  for (let i = 0; i + 1 < entries.length; i += 2) {
29
46
  const key = entries[i];
30
47
  const value = entries[i + 1];
48
+ const quotedKey = ShellQuote.quote([key]);
31
49
  const quotedValue = ShellQuote.quote([value]);
32
- pairs.push(`${key}=${quotedValue}`);
50
+ pairs.push(`${quotedKey}=${quotedValue}`);
33
51
  }
34
52
  return pairs.join(' ');
35
53
  }
@@ -66,6 +84,27 @@ export function splitEnvVarEntries(envVars) {
66
84
  }
67
85
  return entries;
68
86
  }
87
+ /** Returns an error for invalid environment variable entries, or null. */
88
+ export function getEnvVarEntriesValidationError(entries) {
89
+ const seenNames = new Set();
90
+ for (let i = 0; i + 1 < entries.length; i += 2) {
91
+ const name = entries[i].trim();
92
+ const value = entries[i + 1];
93
+ if (name === '' && value !== '') {
94
+ return 'Environment variable name cannot be empty';
95
+ }
96
+ if (name.includes('=')) {
97
+ return `Environment variable name cannot contain "=": ${name}`;
98
+ }
99
+ if (name !== '' && seenNames.has(name)) {
100
+ return `Duplicate environment variable name: ${name}`;
101
+ }
102
+ if (name !== '') {
103
+ seenNames.add(name);
104
+ }
105
+ }
106
+ return null;
107
+ }
69
108
  /**
70
109
  * Joins an array of classpath entries into a colon-separated string.
71
110
  *
@@ -273,6 +273,21 @@ export declare class QueryScheduler {
273
273
  set stopTime(value: number);
274
274
  get overnight(): boolean;
275
275
  set overnight(value: boolean);
276
+ /**
277
+ * Reconciles the base times with the overnight flag when delay-start (useMinStartTime) is
278
+ * enabled on a dependent schedule. While delay-start was off, overnight was a free manual flag
279
+ * and there was no start endpoint; once it becomes live the previously unused start time may be
280
+ * inconsistent with the ordering that validation now enforces.
281
+ *
282
+ * The start time is the field the delay-start checkbox governs, so it is adjusted in preference
283
+ * to the user's chosen End Time and overnight flag:
284
+ * - Non-overnight requires start < stop: if the stale start is not before the End Time, snap it
285
+ * to midnight, preserving the End Time and the overnight flag.
286
+ * - Overnight requires start > stop: there is no clean single start value that keeps the End
287
+ * Time, so fall back to swapping the two real clock values (the only sensible overnight
288
+ * window). This is the one case that changes the End Time.
289
+ */
290
+ alignTimesForDelayStart(): void;
276
291
  get stopTimeDisabled(): boolean;
277
292
  set stopTimeDisabled(value: boolean);
278
293
  /**
@@ -159,6 +159,13 @@ export class QueryScheduler {
159
159
  this.overnightInternal = false;
160
160
  return;
161
161
  }
162
+ // Dependent scheduling only has a start endpoint when delay-start (useMinStartTime) is enabled.
163
+ // While it is disabled there is no ordering constraint at all — both this client validator
164
+ // and the controller skip start/stop ordering — so overnight is a free manual flag and must
165
+ // not be auto-derived from the (unused) base times.
166
+ if (type === QueryScheduler.TYPES.DEPENDENT && !this.useMinStartTime) {
167
+ return;
168
+ }
162
169
  if (!overnightInternal && stopTimeInternal <= startTimeInternal) {
163
170
  this.overnightInternal = true;
164
171
  }
@@ -186,12 +193,45 @@ export class QueryScheduler {
186
193
  set overnight(value) {
187
194
  const { startTimeInternal, stopTimeInternal } = this;
188
195
  this.overnightInternal = value;
196
+ // Dependent scheduling only has a start endpoint when delay-start (useMinStartTime) is enabled.
197
+ // While it is disabled there is no start time to order against, so overnight is a free manual
198
+ // flag and toggling it must not swap the base times. The times are reconciled later, if and
199
+ // when delay-start is enabled, via alignTimesForDelayStart().
200
+ if (this.type === QueryScheduler.TYPES.DEPENDENT && !this.useMinStartTime) {
201
+ return;
202
+ }
189
203
  if ((!value && stopTimeInternal < startTimeInternal) ||
190
204
  (value && startTimeInternal < stopTimeInternal)) {
191
205
  this.startTimeInternal = stopTimeInternal;
192
206
  this.stopTimeInternal = startTimeInternal;
193
207
  }
194
208
  }
209
+ /**
210
+ * Reconciles the base times with the overnight flag when delay-start (useMinStartTime) is
211
+ * enabled on a dependent schedule. While delay-start was off, overnight was a free manual flag
212
+ * and there was no start endpoint; once it becomes live the previously unused start time may be
213
+ * inconsistent with the ordering that validation now enforces.
214
+ *
215
+ * The start time is the field the delay-start checkbox governs, so it is adjusted in preference
216
+ * to the user's chosen End Time and overnight flag:
217
+ * - Non-overnight requires start < stop: if the stale start is not before the End Time, snap it
218
+ * to midnight, preserving the End Time and the overnight flag.
219
+ * - Overnight requires start > stop: there is no clean single start value that keeps the End
220
+ * Time, so fall back to swapping the two real clock values (the only sensible overnight
221
+ * window). This is the one case that changes the End Time.
222
+ */
223
+ alignTimesForDelayStart() {
224
+ const { overnightInternal, startTimeInternal, stopTimeInternal } = this;
225
+ if (!overnightInternal) {
226
+ if (startTimeInternal >= stopTimeInternal) {
227
+ this.startTimeInternal = QueryScheduler.parseLocalTime('00:00:00');
228
+ }
229
+ }
230
+ else if (startTimeInternal <= stopTimeInternal) {
231
+ this.startTimeInternal = stopTimeInternal;
232
+ this.stopTimeInternal = startTimeInternal;
233
+ }
234
+ }
195
235
  get stopTimeDisabled() {
196
236
  return this.stopTimeDisabledInternal;
197
237
  }
@@ -29,6 +29,7 @@ export declare const QUERY_CONFIG_TABLE: "QueryInfo";
29
29
  export declare const SCOPE_CONFIG_TABLE: "ScopeNamesAndType";
30
30
  export declare const CATALOG_TABLE: "catalog";
31
31
  export declare const FACTORY_SERVICE_TABLES: Set<"QueryInfo" | "ScopeNamesAndType" | "catalog">;
32
+ export type FactoryServiceTableName = typeof FACTORY_SERVICE_TABLES extends Set<infer T> ? T : never;
32
33
  export type TemporaryDraftQueryConfig = Omit<DraftQueryConstructorObject, 'scheduling' | 'workerKind' | 'type'> & {
33
34
  type: string;
34
35
  engine: string;
@@ -172,7 +173,7 @@ export declare function hasCommunitySupport(queryConfigurationTypes: ReadonlyMap
172
173
  * @param tableName the name of the table
173
174
  * @returns true if the table is a factory service table, false otherwise
174
175
  */
175
- export declare function isFactoryServiceTable(tableName: string): boolean;
176
+ export declare function isFactoryServiceTable(tableName: string): tableName is FactoryServiceTableName;
176
177
  /**
177
178
  * Turns a URI into a QueryVariableDescriptor
178
179
  * @param params Function parameters
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deephaven-enterprise/query-utils",
3
- "version": "2026.1.54",
3
+ "version": "2026.1.57",
4
4
  "description": "Deephaven Enterprise Query Utils",
5
5
  "author": "Deephaven Data Labs LLC",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -17,11 +17,11 @@
17
17
  "build": "tsc --build"
18
18
  },
19
19
  "dependencies": {
20
- "@deephaven-enterprise/client-utils": "2026.1.54",
21
- "@deephaven-enterprise/jsapi-types": "2026.1.54",
20
+ "@deephaven-enterprise/client-utils": "2026.1.57",
21
+ "@deephaven-enterprise/jsapi-types": "2026.1.57",
22
22
  "@deephaven/jsapi-types": "1.0.0-dev0.40.5",
23
- "@deephaven/log": "^0.97.0",
24
- "@deephaven/utils": "^0.97.0",
23
+ "@deephaven/log": "^1.8.0",
24
+ "@deephaven/utils": "^1.29.0",
25
25
  "@internationalized/date": "^3.12.2",
26
26
  "nanoid": "^5.1.6",
27
27
  "shell-quote": "^1.8.4"