@fatagnus/dink-sdk 2.13.1 → 2.14.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
@@ -81,6 +81,51 @@ const result = await center.call({
81
81
  console.log(result); // { temperature: 22.5 }
82
82
  ```
83
83
 
84
+ ## Loading Credentials
85
+
86
+ Use `loadDinkEnv()` to load credentials from the `.dink/` directory (managed by the `dink env` CLI) with automatic fallback to `DINK_SERVER` / `DINK_API_KEY` environment variables.
87
+
88
+ > **Note:** This is Node.js only (uses `node:fs`, `node:path`). Available via subpath import `@fatagnus/dink-sdk/env` or from the main entry point.
89
+
90
+ ```typescript
91
+ import { loadDinkEnv } from '@fatagnus/dink-sdk/env';
92
+ import { CenterClient, EdgeClient } from '@fatagnus/dink-sdk';
93
+
94
+ // Load current environment (from .dink/.current) or fall back to env vars
95
+ const env = await loadDinkEnv();
96
+
97
+ const center = new CenterClient({ serverUrl: env.server, apiKey: env.apiKey });
98
+ const edge = new EdgeClient({ apiKey: env.apiKey });
99
+ ```
100
+
101
+ Load a specific named environment:
102
+
103
+ ```typescript
104
+ const env = await loadDinkEnv('staging'); // reads .dink/staging.yaml
105
+ ```
106
+
107
+ Credential resolution order:
108
+ 1. **Named env** — `.dink/<name>.yaml` (when a name is passed)
109
+ 2. **Current env** — `.dink/.current` → `.dink/<current>.yaml`
110
+ 3. **Env vars** — `DINK_SERVER` / `DINK_API_KEY` (CI/Docker fallback)
111
+
112
+ Error handling:
113
+
114
+ ```typescript
115
+ import { DinkEnvError } from '@fatagnus/dink-sdk/env';
116
+
117
+ try {
118
+ const env = await loadDinkEnv();
119
+ } catch (e) {
120
+ if (e instanceof DinkEnvError) {
121
+ // e.code === 'NO_ENVIRONMENT' — no .dink/ dir and no env vars set
122
+ // e.code === 'NOT_FOUND' — named env file missing
123
+ }
124
+ }
125
+ ```
126
+
127
+ This integrates with `dink env` CLI commands for managing environments. In CI/Docker where no `.dink/` directory exists, set `DINK_SERVER` and `DINK_API_KEY` as environment variables — the same code works without changes.
128
+
84
129
  ## API Key Formats
85
130
 
86
131
  Dink supports two API key formats for edge authentication:
@@ -188,6 +233,7 @@ You can also import specific clients directly:
188
233
  ```typescript
189
234
  import { EdgeClient } from '@fatagnus/dink-sdk/edge';
190
235
  import { CenterClient } from '@fatagnus/dink-sdk/center';
236
+ import { loadDinkEnv } from '@fatagnus/dink-sdk/env';
191
237
  ```
192
238
 
193
239
  ## Naming Rules
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Credentials loaded from a .dink/ environment or env vars.
3
+ */
4
+ export interface DinkEnv {
5
+ name: string;
6
+ server: string;
7
+ apiKey: string;
8
+ }
9
+ /**
10
+ * Error thrown when environment resolution fails.
11
+ */
12
+ export declare class DinkEnvError extends Error {
13
+ readonly code: 'NO_ENVIRONMENT' | 'NOT_FOUND';
14
+ constructor(message: string, code: 'NO_ENVIRONMENT' | 'NOT_FOUND');
15
+ }
16
+ /**
17
+ * Load Dink credentials.
18
+ *
19
+ * Resolution order:
20
+ * 1. If `name` provided: find `.dink/` dir, read `.dink/<name>.yaml`
21
+ * 2. If no `name`: find `.dink/` dir, read `.dink/.current` for active env name
22
+ * 3. Fall back to `DINK_SERVER` / `DINK_API_KEY` environment variables
23
+ * 4. Throw `DinkEnvError` with code `'NO_ENVIRONMENT'` if nothing found
24
+ */
25
+ export declare function loadDinkEnv(name?: string): Promise<DinkEnv>;
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/env/index.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,GAAG,WAAW,CAAC;gBAElC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,WAAW;CAKlE;AAyCD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCjE"}
@@ -0,0 +1,114 @@
1
+ import { readFile, stat } from 'node:fs/promises';
2
+ import { join, dirname } from 'node:path';
3
+ /**
4
+ * Error thrown when environment resolution fails.
5
+ */
6
+ export class DinkEnvError extends Error {
7
+ code;
8
+ constructor(message, code) {
9
+ super(message);
10
+ this.name = 'DinkEnvError';
11
+ this.code = code;
12
+ }
13
+ }
14
+ /**
15
+ * Walk up from cwd looking for a .dink/ directory.
16
+ * Returns the absolute path to the .dink/ dir, or null if none found.
17
+ */
18
+ async function findDinkDir() {
19
+ let dir = process.cwd();
20
+ for (;;) {
21
+ const candidate = join(dir, '.dink');
22
+ try {
23
+ const info = await stat(candidate);
24
+ if (info.isDirectory()) {
25
+ return candidate;
26
+ }
27
+ }
28
+ catch {
29
+ // not found, keep walking
30
+ }
31
+ const parent = dirname(dir);
32
+ if (parent === dir) {
33
+ // Reached filesystem root.
34
+ return null;
35
+ }
36
+ dir = parent;
37
+ }
38
+ }
39
+ /**
40
+ * Parse a trivial YAML file containing `server` and `api_key` fields.
41
+ * No dependency — files are always simple key: value pairs.
42
+ */
43
+ function parseEnvYaml(content) {
44
+ const serverMatch = content.match(/^server:\s*["']?(.*?)["']?\s*$/m);
45
+ const apiKeyMatch = content.match(/^api_key:\s*["']?(.*?)["']?\s*$/m);
46
+ const server = serverMatch ? serverMatch[1] : '';
47
+ const apiKey = apiKeyMatch ? apiKeyMatch[1] : '';
48
+ return { server, apiKey };
49
+ }
50
+ /**
51
+ * Load Dink credentials.
52
+ *
53
+ * Resolution order:
54
+ * 1. If `name` provided: find `.dink/` dir, read `.dink/<name>.yaml`
55
+ * 2. If no `name`: find `.dink/` dir, read `.dink/.current` for active env name
56
+ * 3. Fall back to `DINK_SERVER` / `DINK_API_KEY` environment variables
57
+ * 4. Throw `DinkEnvError` with code `'NO_ENVIRONMENT'` if nothing found
58
+ */
59
+ export async function loadDinkEnv(name) {
60
+ const dinkDir = await findDinkDir();
61
+ // If a name is explicitly provided, we require a .dink/ directory.
62
+ if (name !== undefined) {
63
+ if (!dinkDir) {
64
+ throw new DinkEnvError(`no .dink/ directory found while looking for environment "${name}"`, 'NOT_FOUND');
65
+ }
66
+ return loadFromFile(dinkDir, name);
67
+ }
68
+ // Try .dink/.current
69
+ if (dinkDir) {
70
+ const currentName = await readCurrentEnv(dinkDir);
71
+ if (currentName) {
72
+ return loadFromFile(dinkDir, currentName);
73
+ }
74
+ }
75
+ // Fall back to env vars
76
+ const server = process.env['DINK_SERVER'];
77
+ const apiKey = process.env['DINK_API_KEY'];
78
+ if (server) {
79
+ return { name: '', server, apiKey: apiKey ?? '' };
80
+ }
81
+ throw new DinkEnvError('no dink environment found: no .dink/ directory, no .current file, and DINK_SERVER is not set', 'NO_ENVIRONMENT');
82
+ }
83
+ /**
84
+ * Read the .dink/.current file. Returns the trimmed env name, or null.
85
+ */
86
+ async function readCurrentEnv(dinkDir) {
87
+ try {
88
+ const data = await readFile(join(dinkDir, '.current'), 'utf-8');
89
+ const trimmed = data.trim();
90
+ return trimmed || null;
91
+ }
92
+ catch {
93
+ return null;
94
+ }
95
+ }
96
+ /**
97
+ * Load and validate a named environment file from `.dink/<name>.yaml`.
98
+ */
99
+ async function loadFromFile(dinkDir, name) {
100
+ const filePath = join(dinkDir, `${name}.yaml`);
101
+ let content;
102
+ try {
103
+ content = await readFile(filePath, 'utf-8');
104
+ }
105
+ catch {
106
+ throw new DinkEnvError(`environment "${name}" not found (expected ${filePath})`, 'NOT_FOUND');
107
+ }
108
+ const { server, apiKey } = parseEnvYaml(content);
109
+ if (!server) {
110
+ throw new Error(`environment "${name}": server field is empty or missing`);
111
+ }
112
+ return { name, server, apiKey };
113
+ }
114
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/env/index.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -3,4 +3,5 @@ export { extractPayload, wrapResponse } from './types.js';
3
3
  export { EdgeClient, parseEdgeKey, type ParsedEdgeKey } from './edge/client.js';
4
4
  export { ServiceBuilder } from './edge/service-builder.js';
5
5
  export { CenterClient } from './center/index.js';
6
+ export { loadDinkEnv, DinkEnvError, type DinkEnv } from './env/index.js';
6
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,YAAY,EAEZ,OAAO,EACP,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,YAAY,EAEZ,OAAO,EACP,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAG3D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,KAAK,OAAO,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -7,4 +7,6 @@ export { EdgeClient, parseEdgeKey } from './edge/client.js';
7
7
  export { ServiceBuilder } from './edge/service-builder.js';
8
8
  // Export center client for connecting to dinkd from the center/cloud
9
9
  export { CenterClient } from './center/index.js';
10
+ // Export environment/credential loader (Node.js only)
11
+ export { loadDinkEnv, DinkEnvError } from './env/index.js';
10
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,2EAA2E;AAiB3E,0BAA0B;AAC1B,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1D,0DAA0D;AAC1D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAsB,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,qEAAqE;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,2EAA2E;AAiB3E,0BAA0B;AAC1B,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1D,0DAA0D;AAC1D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAsB,MAAM,kBAAkB,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,qEAAqE;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,sDAAsD;AACtD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAgB,MAAM,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fatagnus/dink-sdk",
3
- "version": "2.13.1",
3
+ "version": "2.14.0",
4
4
  "description": "TypeScript SDK for Dink edge mesh platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -21,6 +21,10 @@
21
21
  "./center": {
22
22
  "types": "./dist/center/index.d.ts",
23
23
  "import": "./dist/center/index.js"
24
+ },
25
+ "./env": {
26
+ "types": "./dist/env/index.d.ts",
27
+ "import": "./dist/env/index.js"
24
28
  }
25
29
  },
26
30
  "scripts": {