@flytedesk/app-kit 4.0.0 → 5.0.0

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.
@@ -1,4 +1,4 @@
1
- import { type LoadRowsOptions } from "./load.js";
1
+ import { type LoadRowSource, type LoadRowsOptions, type LoadRowsResult } from "./load.js";
2
2
  import type { BigQueryLike, DryRunEstimate, EstimateQueryBytesOptions, ExtractJobHandle, ExtractJobStatus, ExtractTableToGCSInput, JobLabelInput, QueryPage, RunQueryOptions, WaitForExtractJobOptions } from "./types.js";
3
3
  export interface CreateBigQueryReadClientOptions {
4
4
  /** Merged under any per-call `labels`/`extra` — e.g. `{app: "sms-app"}`. */
@@ -11,9 +11,9 @@ export interface BigQueryReadClient {
11
11
  extractTableToGCS(input: ExtractTableToGCSInput): Promise<ExtractJobHandle>;
12
12
  getExtractJobStatus(jobId: string): Promise<ExtractJobStatus>;
13
13
  waitForExtractJob(jobId: string, options?: WaitForExtractJobOptions): Promise<ExtractJobStatus>;
14
- /** Truncate-and-reload (or append) a table from an array of rows — see
15
- * `loadRowsWithRetry`. */
16
- loadRows<T extends object>(options: LoadRowsOptions, rows: T[]): Promise<void>;
14
+ /** Truncate-and-reload (or append) a table from a stream of rows, in memory
15
+ * bounded by the chunk size — see `loadRowsWithRetry`. */
16
+ loadRows<T extends object>(options: LoadRowsOptions, source: LoadRowSource<T>): Promise<LoadRowsResult>;
17
17
  /** Builds a `{actor, purpose, ...extra}` label set, pre-merged with this
18
18
  * client's `defaultLabels` — pass the result as `labels` to `query`/
19
19
  * `extractTableToGCS`. */
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { buildJobLabels } from "./labels.js";
10
10
  import { extractTableToGCS, getExtractJobStatus, waitForExtractJob, } from "./extract.js";
11
- import { loadRowsWithRetry } from "./load.js";
11
+ import { loadRowsWithRetry, } from "./load.js";
12
12
  import { estimateQueryBytes, runQuery, runQueryAllPages } from "./query.js";
13
13
  export function createBigQueryReadClient(bq, options = {}) {
14
14
  const defaultLabels = options.defaultLabels ?? {};
@@ -29,7 +29,7 @@ export function createBigQueryReadClient(bq, options = {}) {
29
29
  extractTableToGCS: (input) => extractTableToGCS(bq, { ...input, labels: withDefaults(input.labels) }),
30
30
  getExtractJobStatus: (jobId) => getExtractJobStatus(bq, jobId),
31
31
  waitForExtractJob: (jobId, waitOptions) => waitForExtractJob(bq, jobId, waitOptions),
32
- loadRows: (loadOptions, rows) => loadRowsWithRetry(bq, { ...loadOptions, labels: withDefaults(loadOptions.labels) }, rows),
32
+ loadRows: (loadOptions, source) => loadRowsWithRetry(bq, { ...loadOptions, labels: withDefaults(loadOptions.labels) }, source),
33
33
  buildJobLabels: (input) => ({
34
34
  ...defaultLabels,
35
35
  ...buildJobLabels(input),
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/bigquery/client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,iBAAiB,EAAwB,MAAM,WAAW,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+C5E,MAAM,UAAU,wBAAwB,CACtC,EAAgB,EAChB,UAA2C,EAAE;IAE7C,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;IAClD,MAAM,YAAY,GAAG,CAAC,MAA+B,EAAE,EAAE,CAAC,CAAC;QACzD,GAAG,aAAa;QAChB,GAAG,MAAM;KACV,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE,CAAC,YAAY,EAAE,EAAE,CACtB,QAAQ,CAAC,EAAE,EAAE;YACX,GAAG,YAAY;YACf,MAAM,EAAE,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC;SAC1C,CAAC;QACJ,aAAa,EAAE,CAAC,YAAY,EAAE,EAAE,CAC9B,gBAAgB,CAAC,EAAE,EAAE;YACnB,GAAG,YAAY;YACf,MAAM,EAAE,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC;SAC1C,CAAC;QACJ,kBAAkB,EAAE,CAAC,eAAe,EAAE,EAAE,CACtC,kBAAkB,CAAC,EAAE,EAAE,eAAe,CAAC;QACzC,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAC3B,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACzE,mBAAmB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,CAAC,EAAE,EAAE,KAAK,CAAC;QAC9D,iBAAiB,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,CACxC,iBAAiB,CAAC,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC;QAC3C,QAAQ,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,EAAE,CAC9B,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,WAAW,EAAE,MAAM,EAAE,YAAY,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC;QAC3F,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC1B,GAAG,aAAa;YAChB,GAAG,cAAc,CAAC,KAAK,CAAC;SACzB,CAAC;KACH,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/bigquery/client.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,iBAAiB,GAIlB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+C5E,MAAM,UAAU,wBAAwB,CACtC,EAAgB,EAChB,UAA2C,EAAE;IAE7C,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,EAAE,CAAC;IAClD,MAAM,YAAY,GAAG,CAAC,MAA+B,EAAE,EAAE,CAAC,CAAC;QACzD,GAAG,aAAa;QAChB,GAAG,MAAM;KACV,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE,CAAC,YAAY,EAAE,EAAE,CACtB,QAAQ,CAAC,EAAE,EAAE;YACX,GAAG,YAAY;YACf,MAAM,EAAE,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC;SAC1C,CAAC;QACJ,aAAa,EAAE,CAAC,YAAY,EAAE,EAAE,CAC9B,gBAAgB,CAAC,EAAE,EAAE;YACnB,GAAG,YAAY;YACf,MAAM,EAAE,YAAY,CAAC,YAAY,CAAC,MAAM,CAAC;SAC1C,CAAC;QACJ,kBAAkB,EAAE,CAAC,eAAe,EAAE,EAAE,CACtC,kBAAkB,CAAC,EAAE,EAAE,eAAe,CAAC;QACzC,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAC3B,iBAAiB,CAAC,EAAE,EAAE,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QACzE,mBAAmB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,mBAAmB,CAAC,EAAE,EAAE,KAAK,CAAC;QAC9D,iBAAiB,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,CACxC,iBAAiB,CAAC,EAAE,EAAE,KAAK,EAAE,WAAW,CAAC;QAC3C,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,CAChC,iBAAiB,CACf,EAAE,EACF,EAAE,GAAG,WAAW,EAAE,MAAM,EAAE,YAAY,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,EAC5D,MAAM,CACP;QACH,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC1B,GAAG,aAAa;YAChB,GAAG,cAAc,CAAC,KAAK,CAAC;SACzB,CAAC;KACH,CAAC;AACJ,CAAC"}
@@ -45,8 +45,8 @@ export type { BigQueryReadClient, CreateBigQueryReadClientOptions, } from "./cli
45
45
  export { runQuery, runQueryAllPages, estimateQueryBytes } from "./query.js";
46
46
  export { extractTableToGCS, getExtractJobStatus, waitForExtractJob, } from "./extract.js";
47
47
  export { loadRowsWithRetry } from "./load.js";
48
- export type { LoadRowsOptions } from "./load.js";
48
+ export type { LoadRowSource, LoadRowsOptions, LoadRowsResult } from "./load.js";
49
49
  export { buildJobLabels, sanitizeLabelValue } from "./labels.js";
50
50
  export { unwrapBigQueryCount, unwrapBigQueryValue } from "./values.js";
51
51
  export { BadQueryError, BigQueryReadError, DryRunBudgetExceededError, MaximumBytesBilledExceededError, PermissionDeniedError, RateLimitExceededError, classifyBigQueryError, isRetryableBigQueryError, } from "./errors.js";
52
- export type { BigQueryApiErrorDetail, BigQueryCreateQueryJobOptions, BigQueryDatasetLike, BigQueryExtractOptions, BigQueryJobLike, BigQueryJobMetadata, BigQueryLike, BigQueryLoadOptions, BigQueryNextQuery, BigQueryQueryResultsOptions, BigQuerySchemaField, BigQueryTableLike, BigQueryTableReference, BigQueryWriteStreamLike, DryRunEstimate, EstimateQueryBytesOptions, ExtractJobHandle, ExtractJobState, ExtractJobStatus, ExtractTableToGCSInput, GcsFileLike, JobLabelInput, QueryPage, RunQueryOptions, WaitForExtractJobOptions, } from "./types.js";
52
+ export type { BigQueryApiErrorDetail, BigQueryCreateQueryJobOptions, BigQueryDatasetLike, BigQueryExtractOptions, BigQueryJobLike, BigQueryJobMetadata, BigQueryLike, BigQueryLoadOptions, BigQueryNextQuery, BigQueryQueryResultsOptions, BigQuerySchemaField, BigQueryTableLike, BigQueryTableReference, DryRunEstimate, EstimateQueryBytesOptions, ExtractJobHandle, ExtractJobState, ExtractJobStatus, ExtractTableToGCSInput, GcsFileLike, JobLabelInput, QueryPage, RunQueryOptions, WaitForExtractJobOptions, } from "./types.js";
@@ -1,4 +1,12 @@
1
1
  import type { BigQueryLike, BigQueryLoadOptions } from "./types.js";
2
+ /**
3
+ * Opens the rows for one load attempt. Called once per attempt: a retry after
4
+ * a rate-limit error re-uploads everything, so it calls this again and must
5
+ * get the same rows. That is why the source is a function and not an
6
+ * iterable — a generator object can only be consumed once, so a retry handed
7
+ * the same one would silently load nothing.
8
+ */
9
+ export type LoadRowSource<T extends object> = () => AsyncIterable<T> | Iterable<T>;
2
10
  export interface LoadRowsOptions extends BigQueryLoadOptions {
3
11
  datasetId: string;
4
12
  tableId: string;
@@ -6,17 +14,27 @@ export interface LoadRowsOptions extends BigQueryLoadOptions {
6
14
  attempts?: number;
7
15
  /** Delay before the first retry; doubles every subsequent retry. Default 1000ms. */
8
16
  initialDelayMs?: number;
17
+ /** Target size of each NDJSON chunk written to the load job, in bytes. The
18
+ * helper holds at most about one chunk being built plus what the write
19
+ * stream itself buffers. A single row larger than this is written as its
20
+ * own chunk. Default 1 MiB. */
21
+ chunkBytes?: number;
9
22
  /** Injectable for tests — real code always uses a real timer. */
10
23
  sleep?: (ms: number) => Promise<void>;
11
24
  }
25
+ export interface LoadRowsResult {
26
+ /** Rows the successful attempt uploaded. */
27
+ rowCount: number;
28
+ }
12
29
  /**
13
- * Loads `rows` into `datasetId.tableId` in one load job, retrying with
14
- * exponential backoff ONLY when the failure is BigQuery's
30
+ * Streams the rows `source` yields into `datasetId.tableId` in one load job,
31
+ * retrying with exponential backoff ONLY when the failure is BigQuery's
15
32
  * rateLimitExceeded/quotaExceeded class of error (see
16
- * `isRetryableBigQueryError`). Any other error (bad schema, auth failure,
17
- * etc.) is classified and rethrown on the first attempt.
33
+ * `isRetryableBigQueryError`). Any other error (bad schema, auth failure, a
34
+ * throw from the source, etc.) is classified and rethrown on the first
35
+ * attempt. Each attempt calls `source` afresh — see `LoadRowSource`.
18
36
  *
19
- * Pass `writeDisposition: "WRITE_TRUNCATE"` for a full-refresh reload (the
20
- * IPEDS institutions loader's use case); omit it for an append load.
37
+ * Pass `writeDisposition: "WRITE_TRUNCATE"` for a full-refresh reload; omit it
38
+ * for an append load.
21
39
  */
22
- export declare function loadRowsWithRetry<T extends object>(bq: BigQueryLike, options: LoadRowsOptions, rows: T[]): Promise<void>;
40
+ export declare function loadRowsWithRetry<T extends object>(bq: BigQueryLike, options: LoadRowsOptions, source: LoadRowSource<T>): Promise<LoadRowsResult>;
@@ -1,61 +1,91 @@
1
1
  /**
2
- * Load-job capability (AK-14 follow-on, MP-200/PLN-2): truncate-and-reload a
3
- * BigQuery table from an array of rows, with retry-with-backoff limited to
2
+ * Load-job capability (AK-14 follow-on, MP-200/PLN-2): loads a stream of rows
3
+ * into a BigQuery table in one load job, with retry-with-backoff limited to
4
4
  * BigQuery's own rate-limit/quota class of error — generalized out of
5
5
  * media-planner's `apps/api/src/lib/bigqueryLoad.ts` (via flytedesk-id's
6
6
  * verbatim port at `src/lib/bigqueryLoad.ts`, MP-200), which this module
7
- * replaces. Callers needing a periodic full-refresh load (e.g. the IPEDS
8
- * institutions loader) use this instead of keeping their own copy of the
9
- * write-stream + retry plumbing.
7
+ * replaces.
8
+ *
9
+ * Memory is bounded by `chunkBytes`, never by the row count (AK-28): rows are
10
+ * pulled from the caller's source one at a time, serialized into NDJSON chunks
11
+ * of about `chunkBytes`, and piped into the load job's write stream with
12
+ * `stream.pipeline`, which stops pulling from the source whenever the write
13
+ * stream's buffer is full and resumes on its `drain`. A caller that reads its
14
+ * rows page by page (a keyset-paged or cursor read) can therefore load any
15
+ * number of rows in a fixed amount of memory.
10
16
  */
11
- import { BigQueryReadError, classifyBigQueryError, isRetryableBigQueryError } from "./errors.js";
17
+ import { once } from "node:events";
18
+ import { pipeline } from "node:stream/promises";
19
+ import { BigQueryReadError, classifyBigQueryError, isRetryableBigQueryError, } from "./errors.js";
12
20
  const DEFAULT_ATTEMPTS = 5;
13
21
  const DEFAULT_INITIAL_DELAY_MS = 1_000;
22
+ const DEFAULT_CHUNK_BYTES = 1024 * 1024;
14
23
  const DEFAULT_SOURCE_FORMAT = "NEWLINE_DELIMITED_JSON";
15
24
  function defaultSleep(ms) {
16
25
  return new Promise((resolve) => setTimeout(resolve, ms));
17
26
  }
18
- async function runLoadJob(bq, options, rows) {
27
+ /** Serializes `rows` into NDJSON, one `Buffer` of about `chunkBytes` at a
28
+ * time, counting rows into `counter` as they are consumed. */
29
+ async function* ndjsonChunks(rows, chunkBytes, counter) {
30
+ let lines = [];
31
+ let size = 0;
32
+ for await (const row of rows) {
33
+ const line = `${JSON.stringify(row)}\n`;
34
+ lines.push(line);
35
+ size += Buffer.byteLength(line);
36
+ counter.rows += 1;
37
+ if (size >= chunkBytes) {
38
+ yield Buffer.from(lines.join(""));
39
+ lines = [];
40
+ size = 0;
41
+ }
42
+ }
43
+ if (lines.length > 0)
44
+ yield Buffer.from(lines.join(""));
45
+ }
46
+ async function runLoadJob(bq, options, source) {
19
47
  const table = bq.dataset(options.datasetId).table(options.tableId);
20
48
  const createWriteStream = table.createWriteStream;
21
49
  if (!createWriteStream) {
22
50
  throw new BigQueryReadError(`dataset(${options.datasetId}).table(${options.tableId}) does not implement createWriteStream — this BigQueryLike was not constructed for load jobs`);
23
51
  }
24
- const ndjson = rows.map((row) => `${JSON.stringify(row)}\n`);
25
- await new Promise((resolve, reject) => {
26
- const stream = createWriteStream.call(table, {
27
- sourceFormat: options.sourceFormat ?? DEFAULT_SOURCE_FORMAT,
28
- schema: options.schema,
29
- writeDisposition: options.writeDisposition,
30
- createDisposition: options.createDisposition,
31
- labels: options.labels,
32
- jobId: options.jobId,
33
- });
34
- stream.on("error", reject);
35
- stream.on("complete", () => resolve());
36
- for (const line of ndjson)
37
- stream.write(line);
38
- stream.end();
52
+ const stream = createWriteStream.call(table, {
53
+ sourceFormat: options.sourceFormat ?? DEFAULT_SOURCE_FORMAT,
54
+ schema: options.schema,
55
+ writeDisposition: options.writeDisposition,
56
+ createDisposition: options.createDisposition,
57
+ labels: options.labels,
58
+ jobId: options.jobId,
39
59
  });
60
+ const counter = { rows: 0 };
61
+ // The load is done only when BigQuery reports the job "complete" AND the
62
+ // pipeline has flushed every chunk. `once` rejects on the stream's "error",
63
+ // which is how a failed job surfaces; `pipeline` rejects (and destroys the
64
+ // stream, aborting the upload) when the source itself throws.
65
+ await Promise.all([
66
+ once(stream, "complete"),
67
+ pipeline(ndjsonChunks(source(), options.chunkBytes ?? DEFAULT_CHUNK_BYTES, counter), stream),
68
+ ]);
69
+ return { rowCount: counter.rows };
40
70
  }
41
71
  /**
42
- * Loads `rows` into `datasetId.tableId` in one load job, retrying with
43
- * exponential backoff ONLY when the failure is BigQuery's
72
+ * Streams the rows `source` yields into `datasetId.tableId` in one load job,
73
+ * retrying with exponential backoff ONLY when the failure is BigQuery's
44
74
  * rateLimitExceeded/quotaExceeded class of error (see
45
- * `isRetryableBigQueryError`). Any other error (bad schema, auth failure,
46
- * etc.) is classified and rethrown on the first attempt.
75
+ * `isRetryableBigQueryError`). Any other error (bad schema, auth failure, a
76
+ * throw from the source, etc.) is classified and rethrown on the first
77
+ * attempt. Each attempt calls `source` afresh — see `LoadRowSource`.
47
78
  *
48
- * Pass `writeDisposition: "WRITE_TRUNCATE"` for a full-refresh reload (the
49
- * IPEDS institutions loader's use case); omit it for an append load.
79
+ * Pass `writeDisposition: "WRITE_TRUNCATE"` for a full-refresh reload; omit it
80
+ * for an append load.
50
81
  */
51
- export async function loadRowsWithRetry(bq, options, rows) {
82
+ export async function loadRowsWithRetry(bq, options, source) {
52
83
  const attempts = options.attempts ?? DEFAULT_ATTEMPTS;
53
84
  const initialDelayMs = options.initialDelayMs ?? DEFAULT_INITIAL_DELAY_MS;
54
85
  const sleep = options.sleep ?? defaultSleep;
55
86
  for (let attempt = 1;; attempt += 1) {
56
87
  try {
57
- await runLoadJob(bq, options, rows);
58
- return;
88
+ return await runLoadJob(bq, options, source);
59
89
  }
60
90
  catch (err) {
61
91
  if (attempt >= attempts || !isRetryableBigQueryError(err)) {
@@ -1 +1 @@
1
- {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/bigquery/load.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/bigquery/load.ts"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@
11
11
  * `@google-cloud/bigquery` calls it made; inventory-specific SQL stays behind
12
12
  * in media-planner.
13
13
  */
14
+ import type { Writable } from "node:stream";
14
15
  /** One structured error entry, same shape the Google APIs client library
15
16
  * attaches to a rejected job promise's `.errors` array. */
16
17
  export interface BigQueryApiErrorDetail {
@@ -109,15 +110,6 @@ export interface GcsFileLike {
109
110
  readonly name: string;
110
111
  };
111
112
  }
112
- /** Structural subset of Node's own Writable that `Table#createWriteStream`
113
- * needs to expose — the "error"/"complete" events a load-job caller pipes
114
- * rows into and waits on. */
115
- export interface BigQueryWriteStreamLike {
116
- on(event: "error", listener: (err: unknown) => void): this;
117
- on(event: "complete", listener: () => void): this;
118
- write(chunk: string): boolean;
119
- end(): void;
120
- }
121
113
  /** One BigQuery table-schema field, as `createWriteStream`'s `schema` option
122
114
  * (and the REST API's `tables.insert`) accepts it. `fields` recurses for
123
115
  * RECORD/STRUCT columns. */
@@ -143,12 +135,15 @@ export interface BigQueryLoadOptions {
143
135
  }
144
136
  export interface BigQueryTableLike {
145
137
  createExtractJob(destination: GcsFileLike, options?: BigQueryExtractOptions): Promise<[BigQueryJobLike, BigQueryJobMetadata]>;
146
- /** Opens a load-job write stream — the caller pipes newline-delimited rows
147
- * (or other `sourceFormat`) into it, then awaits the stream's "complete"/
148
- * "error" event. Mirrors `@google-cloud/bigquery`'s `Table#createWriteStream`.
149
- * Optional: read-only callers (query/extract) never need it — only
150
- * `loadRowsWithRetry` (./load.js) does, and it fails loudly if missing. */
151
- createWriteStream?(options: BigQueryLoadOptions): BigQueryWriteStreamLike;
138
+ /** Opens a load-job write stream — a real Node `Writable`, since the loader
139
+ * pipes NDJSON into it with `stream.pipeline` and relies on its
140
+ * backpressure (`write()` returning false, then "drain"). The stream emits
141
+ * "complete" once BigQuery finishes the job, or "error" if the job fails.
142
+ * Mirrors `@google-cloud/bigquery`'s `Table#createWriteStream`, which
143
+ * returns a `Writable`. Optional: read-only callers (query/extract) never
144
+ * need it — only `loadRowsWithRetry` (./load.js) does, and it fails loudly
145
+ * if missing. */
146
+ createWriteStream?(options: BigQueryLoadOptions): Writable;
152
147
  }
153
148
  export interface BigQueryDatasetLike {
154
149
  table(id: string): BigQueryTableLike;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flytedesk/app-kit",
3
- "version": "4.0.0",
3
+ "version": "5.0.0",
4
4
  "description": "Shared platform kit for flytedesk apps: flytedesk-id auth (BFF/OIDC client) and a Postgres-native trace/audit layer.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",