@valtrix/sdk 0.1.0 → 0.2.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.
package/README.md CHANGED
@@ -32,6 +32,12 @@ for await (const row of valtrix.customersClean.iterate()) {
32
32
 
33
33
  Rows come from every organization you hold an active grant for. Pass `org` to narrow to one organization. Filters, ordering, and pagination run server side.
34
34
 
35
+ Every row carries a stable `_record_id`. Store it and you can fetch the same row back later as a point lookup:
36
+
37
+ ```ts
38
+ const [invoice] = await valtrix.invoicesClean.findMany({ where: { _record_id: 'rec_9f2k7d' } })
39
+ ```
40
+
35
41
  ## Writing records
36
42
 
37
43
  Writes create records, not rows. They flow through your published transformation like any other source, so they arrive in your tables cleaned, deduped, and quality checked.
@@ -46,12 +52,14 @@ await valtrix.records.customer.upsert({
46
52
 
47
53
  The response includes the derived table rows, so you read your write already transformed. A row that fails an error-severity quality rule is reported as quarantined instead of landing in the table.
48
54
 
49
- ## Syncing changes
55
+ ## Reacting to changes
56
+
57
+ Your tables are the database, so there is nothing to copy or keep in sync. The change feed is how you react when data changes: kick off a workflow, notify someone, refresh something you derived.
50
58
 
51
59
  ```ts
52
60
  for await (const change of valtrix.customersClean.changes({ cursor })) {
53
- if (change.type === 'upsert') await warehouse.upsert(change.row)
54
- else await warehouse.remove(change.recordId)
61
+ if (change.type === 'upsert') await notifyAccountManager(change.row)
62
+ else await closeOpenTasks(change.recordId)
55
63
  cursor = change.cursor
56
64
  }
57
65
  ```
package/dist/cli.d.ts CHANGED
@@ -3,4 +3,6 @@ export type CliIo = {
3
3
  error: (message: string) => void;
4
4
  fetchImpl?: typeof fetch;
5
5
  };
6
+ export declare function parseEnvFile(content: string): Record<string, string>;
7
+ export declare function loadEnvFiles(cwd: string): Promise<Record<string, string>>;
6
8
  export declare function runCli(argv: string[], env: Record<string, string | undefined>, io: CliIo): Promise<number>;
package/dist/cli.js CHANGED
@@ -2,7 +2,42 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { emitClientSource } from './codegen.js';
4
4
  const DEFAULT_OUT = path.join('valtrix', 'client.ts');
5
- const DEFAULT_BASE_URL = 'https://api.valtrix.com';
5
+ const DEFAULT_BASE_URL = 'https://api.valtrix.vojtadrmota.com';
6
+ const ENV_FILES = ['.env', '.env.local'];
7
+ export function parseEnvFile(content) {
8
+ const values = {};
9
+ for (const line of content.split(/\r?\n/)) {
10
+ const trimmed = line.trim();
11
+ if (!trimmed || trimmed.startsWith('#'))
12
+ continue;
13
+ const withoutExport = trimmed.startsWith('export ') ? trimmed.slice(7).trim() : trimmed;
14
+ const separator = withoutExport.indexOf('=');
15
+ if (separator === -1)
16
+ continue;
17
+ const key = withoutExport.slice(0, separator).trim();
18
+ if (!key)
19
+ continue;
20
+ let value = withoutExport.slice(separator + 1).trim();
21
+ const quote = value[0];
22
+ if ((quote === '"' || quote === "'") && value.endsWith(quote) && value.length >= 2) {
23
+ value = value.slice(1, -1);
24
+ }
25
+ values[key] = value;
26
+ }
27
+ return values;
28
+ }
29
+ export async function loadEnvFiles(cwd) {
30
+ const merged = {};
31
+ for (const file of ENV_FILES) {
32
+ try {
33
+ Object.assign(merged, parseEnvFile(await readFile(path.join(cwd, file), 'utf8')));
34
+ }
35
+ catch {
36
+ continue;
37
+ }
38
+ }
39
+ return merged;
40
+ }
6
41
  function parseArgs(argv) {
7
42
  const flags = new Map();
8
43
  let command = null;
@@ -32,18 +67,21 @@ export async function runCli(argv, env, io) {
32
67
  io.log('');
33
68
  io.log('Reads your published table schemas and writes a typed client.');
34
69
  io.log('Environment: VALTRIX_API_KEY (required), VALTRIX_API_URL (optional).');
70
+ io.log('Both are read from the shell, or from .env.local / .env in the current directory.');
35
71
  return command ? 0 : 1;
36
72
  }
37
73
  if (command !== 'generate') {
38
74
  io.error(`Unknown command "${command}". Run "valtrix help".`);
39
75
  return 1;
40
76
  }
41
- const apiKey = env.VALTRIX_API_KEY;
77
+ const fileEnv = await loadEnvFiles(process.cwd());
78
+ const apiKey = env.VALTRIX_API_KEY ?? fileEnv.VALTRIX_API_KEY;
42
79
  if (!apiKey) {
43
80
  io.error('Set VALTRIX_API_KEY to a key from your Valtrix console (Developers, API Keys).');
81
+ io.error('Export it, or put it in a gitignored .env.local or .env next to your project.');
44
82
  return 1;
45
83
  }
46
- const baseUrl = flags.get('url') ?? env.VALTRIX_API_URL ?? DEFAULT_BASE_URL;
84
+ const baseUrl = flags.get('url') ?? env.VALTRIX_API_URL ?? fileEnv.VALTRIX_API_URL ?? DEFAULT_BASE_URL;
47
85
  const outPath = flags.get('out') ?? DEFAULT_OUT;
48
86
  const checkOnly = flags.get('check') === true;
49
87
  const fetchImpl = io.fetchImpl ?? fetch;
package/dist/client.d.ts CHANGED
@@ -16,8 +16,14 @@ export type WhereOperators<V> = {
16
16
  not?: null;
17
17
  };
18
18
  export type WhereValue<V> = V | null | WhereOperators<NonNullable<V>>;
19
+ export type RecordIdFilter = string | {
20
+ eq?: string;
21
+ in?: string[];
22
+ };
19
23
  export type Where<Row> = {
20
24
  [K in keyof Row]?: WhereValue<Row[K]>;
25
+ } & {
26
+ _record_id?: RecordIdFilter;
21
27
  };
22
28
  export type OrderBy<Row> = {
23
29
  [K in keyof Row]?: 'asc' | 'desc';
package/dist/client.js CHANGED
@@ -196,7 +196,7 @@ export class ConnectApi {
196
196
  return { connectUrl: response.connect_url, expiresAt: response.expires_at };
197
197
  }
198
198
  }
199
- const DEFAULT_BASE_URL = 'https://api.valtrix.com';
199
+ const DEFAULT_BASE_URL = 'https://api.valtrix.vojtadrmota.com';
200
200
  function resolveEnv(name) {
201
201
  if (typeof process === 'undefined')
202
202
  return undefined;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export { ConnectApi, createClient, RecordHandle, TableHandle, } from './client.js';
2
- export type { ClientShape, CreateClientConfig, FindManyQuery, MetaFields, OrderBy, RowsPage, TableChange, TableWriteOutcome, UpsertResult, ValtrixClient, Where, WhereOperators, WhereValue, } from './client.js';
2
+ export type { ClientShape, CreateClientConfig, FindManyQuery, MetaFields, OrderBy, RecordIdFilter, RowsPage, TableChange, TableWriteOutcome, UpsertResult, ValtrixClient, Where, WhereOperators, WhereValue, } from './client.js';
3
3
  export { errorFromResponse, ValtrixAuthError, ValtrixCursorExpiredError, ValtrixError, ValtrixRateLimitError, } from './errors.js';
4
4
  export type { FieldIssue } from './errors.js';
5
5
  export { camelCase, emitClientSource, pascalCase } from './codegen.js';
6
6
  export type { GeneratedClient } from './codegen.js';
7
7
  export { runCli } from './cli.js';
8
- export type { ApiChange, ApiChangesResponse, ApiColumn, ApiConnectSessionResponse, ApiRecordType, ApiRowsResponse, ApiSchema, ApiTable, ApiUpsertResponse, } from './protocol.js';
8
+ export type { ApiChange, ApiChangesResponse, ApiColumn, ApiConnectSessionResponse, ApiDeleteResponse, ApiRecord, ApiRecordResponse, ApiRecordType, ApiRowsResponse, ApiSchema, ApiTable, ApiTableOutcome, ApiUpsertResponse, } from './protocol.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@valtrix/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Typed client and codegen CLI for the Valtrix Tables API. Run valtrix generate to get a client typed against your published tables.",
5
5
  "license": "MIT",
6
6
  "type": "module",