tplm-lang 0.3.2 → 0.3.4

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.
package/README.md CHANGED
@@ -84,6 +84,16 @@ const tpl = fromBigQueryTable({
84
84
  credentialsPath: "./credentials.json",
85
85
  });
86
86
 
87
+ // Pre-built connection (any Malloy Connection instance)
88
+ import { fromConnection } from "tplm-lang";
89
+ import { BigQueryConnection } from "@malloydata/db-bigquery";
90
+ const conn = new BigQueryConnection("bigquery", {}, { credentials });
91
+ const tpl = fromConnection({
92
+ connection: conn,
93
+ table: "project.dataset.sales",
94
+ dialect: "bigquery",
95
+ });
96
+
87
97
  // Then query the same way
88
98
  const { html } = await tpl.query("TABLE ROWS region * revenue.sum;");
89
99
  ```
@@ -184,18 +194,29 @@ COLS education * (revenue.sum ACROSS COLS | revenue.mean)
184
194
  ### Easy Connectors (Recommended)
185
195
 
186
196
  ```typescript
187
- import { fromCSV, fromDuckDBTable, fromBigQueryTable } from "tplm-lang";
197
+ import {
198
+ fromCSV, fromDuckDBTable, fromBigQueryTable, fromConnection
199
+ } from "tplm-lang";
188
200
 
189
201
  // CSV or Parquet files
190
202
  const tpl = fromCSV("data/sales.csv");
191
203
  const tpl = fromDuckDBTable("data/sales.parquet");
192
204
 
193
- // BigQuery
205
+ // BigQuery (file-based credentials)
194
206
  const tpl = fromBigQueryTable({
195
207
  table: "project.dataset.sales",
196
208
  credentialsPath: "./credentials.json",
197
209
  });
198
210
 
211
+ // Pre-built connection (inject any Malloy Connection instance)
212
+ import { BigQueryConnection } from "@malloydata/db-bigquery";
213
+ const conn = new BigQueryConnection("bigquery", {}, { credentials });
214
+ const tpl = fromConnection({
215
+ connection: conn,
216
+ table: "project.dataset.sales",
217
+ dialect: "bigquery", // or "duckdb"
218
+ });
219
+
199
220
  // Add computed dimensions with .extend()
200
221
  const tplWithDims = tpl.extend(`
201
222
  dimension: region is pick 'North' when region_code = 1 else 'South'
@@ -16,10 +16,17 @@ let currentOrderingProvider;
16
16
  // DATE NORMALIZATION
17
17
  // ---
18
18
  /**
19
- * Normalize a raw data value: converts Date objects to formatted strings
19
+ * Normalize a raw data value: converts date-like objects to formatted strings
20
20
  * so they display cleanly as headers and work correctly with Set-based
21
21
  * deduplication and value-based cell lookup.
22
22
  *
23
+ * Handles two cases:
24
+ * 1. JS Date objects (from DuckDB connector)
25
+ * 2. BigQuery SDK wrapper objects (BigQueryDate, BigQueryTimestamp,
26
+ * BigQueryDatetime) which are plain objects with a `.value` string
27
+ * property and no custom toString() — producing "[object Object]"
28
+ * if left unconverted.
29
+ *
23
30
  * Dates at midnight UTC (date-only) become "YYYY-MM-DD".
24
31
  * Timestamps with a time component become "YYYY-MM-DD HH:MM:SS".
25
32
  */
@@ -30,19 +37,38 @@ function normalizeDimValue(value) {
30
37
  ? iso.slice(0, 10)
31
38
  : iso.slice(0, 19).replace("T", " ");
32
39
  }
40
+ // Handle BigQuery SDK date types (BigQueryDate, BigQueryTimestamp, etc.)
41
+ // These are plain objects with a .value string property.
42
+ if (value !== null &&
43
+ typeof value === "object" &&
44
+ !Array.isArray(value) &&
45
+ "value" in value &&
46
+ typeof value.value === "string") {
47
+ const str = value.value;
48
+ // ISO timestamp at midnight → date-only
49
+ if (/^\d{4}-\d{2}-\d{2}T00:00:00(\.0+)?Z?$/.test(str)) {
50
+ return str.slice(0, 10);
51
+ }
52
+ // ISO timestamp with time → "YYYY-MM-DD HH:MM:SS"
53
+ if (/^\d{4}-\d{2}-\d{2}T/.test(str)) {
54
+ return str.slice(0, 19).replace("T", " ");
55
+ }
56
+ // Already formatted (BigQueryDate "2025-08-01", BigQueryDatetime "2025-08-01 00:00:00")
57
+ return str;
58
+ }
33
59
  return value;
34
60
  }
35
- /**
36
- * Recursively normalize all Date values in query result rows.
37
- * This ensures Date objects are converted to formatted strings before
38
- * any downstream processing (header extraction, cell indexing, etc.).
39
- */
61
+ /** Check if an object is a date-like wrapper (BigQuery SDK types) vs a nested row */
62
+ function isDateLikeWrapper(obj) {
63
+ return "value" in obj && typeof obj.value === "string";
64
+ }
40
65
  function normalizeResultRow(row) {
41
66
  const normalized = {};
42
67
  for (const key of Object.keys(row)) {
43
68
  const val = row[key];
44
69
  if (Array.isArray(val)) {
45
- normalized[key] = val.map((item) => item && typeof item === "object" && !Array.isArray(item) && !(item instanceof Date)
70
+ normalized[key] = val.map((item) => item && typeof item === "object" && !Array.isArray(item)
71
+ && !(item instanceof Date) && !isDateLikeWrapper(item)
46
72
  ? normalizeResultRow(item)
47
73
  : normalizeDimValue(item));
48
74
  }
@@ -50,6 +50,12 @@ export declare function createLocalConnection(): Promise<Connection>;
50
50
  */
51
51
  export declare function getConnection(): Promise<Connection>;
52
52
  export declare function getConnectionType(): ConnectionType | null;
53
+ /**
54
+ * Inject a pre-built Malloy connection directly.
55
+ * Use this when you already have a configured Connection instance
56
+ * (e.g., a BigQueryConnection with in-memory credentials).
57
+ */
58
+ export declare function setConnection(conn: Connection, type: ConnectionType): void;
53
59
  export declare function listDatasets(): Promise<string[]>;
54
60
  export declare function listTables(dataset: string): Promise<{
55
61
  name: string;
@@ -80,4 +86,4 @@ export declare function executeMalloy(malloySource: string, options?: ExecuteOpt
80
86
  */
81
87
  export declare function executeSQL(sql: string): Promise<any[]>;
82
88
  export { Runtime };
83
- export type { DuckDBConnection, BigQueryConnection };
89
+ export type { Connection, DuckDBConnection, BigQueryConnection };
@@ -134,6 +134,16 @@ export function getConnectionType() {
134
134
  }
135
135
  return currentConnectionType;
136
136
  }
137
+ /**
138
+ * Inject a pre-built Malloy connection directly.
139
+ * Use this when you already have a configured Connection instance
140
+ * (e.g., a BigQueryConnection with in-memory credentials).
141
+ */
142
+ export function setConnection(conn, type) {
143
+ connectionInstance = conn;
144
+ currentConnectionType = type;
145
+ pendingOptions = null;
146
+ }
137
147
  // ---
138
148
  // SCHEMA EXPLORATION (BigQuery only)
139
149
  // ---