@zeph-to/hook-sdk 0.1.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 ADDED
@@ -0,0 +1,105 @@
1
+ # @zeph/hook-sdk
2
+
3
+ Push notification SDK + CLI for [Zeph](https://zeph.to). Zero dependencies — uses native `fetch`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @zeph/hook-sdk
9
+ ```
10
+
11
+ ## SDK Usage
12
+
13
+ ```typescript
14
+ import { ZephHook } from '@zeph/hook-sdk';
15
+
16
+ const hook = new ZephHook({ apiKey: 'ak_...' });
17
+
18
+ const result = await hook.notify({
19
+ title: 'Build Complete',
20
+ body: 'Deploy succeeded',
21
+ url: 'https://example.com/deploy/123',
22
+ });
23
+
24
+ console.log(result.pushId); // 'push_01JXY...'
25
+ ```
26
+
27
+ ### Options
28
+
29
+ ```typescript
30
+ const hook = new ZephHook({
31
+ apiKey: 'ak_...', // Required — API key from Zeph settings
32
+ baseUrl: 'https://...', // Optional — API base URL (default: https://api.zeph.to)
33
+ timeout: 30000, // Optional — request timeout in ms (default: 30000)
34
+ });
35
+ ```
36
+
37
+ ### Notify Payload
38
+
39
+ | Field | Type | Description |
40
+ |-------|------|-------------|
41
+ | `title` | `string?` | Push title |
42
+ | `body` | `string?` | Push body |
43
+ | `url` | `string?` | URL to include |
44
+ | `type` | `'note' \| 'link' \| 'file'?` | Push type (default: `note`) |
45
+ | `targetDeviceId` | `string?` | Send to specific device |
46
+
47
+ ### Error Handling
48
+
49
+ ```typescript
50
+ import { ZephHook, AuthenticationError, QuotaExceededError, ZephError } from '@zeph/hook-sdk';
51
+
52
+ try {
53
+ await hook.notify({ title: 'Hello' });
54
+ } catch (err) {
55
+ if (err instanceof AuthenticationError) { /* Invalid API key */ }
56
+ if (err instanceof QuotaExceededError) { /* Monthly limit reached */ }
57
+ if (err instanceof ZephError) { /* Other API error */ }
58
+ }
59
+ ```
60
+
61
+ ## CLI Usage
62
+
63
+ ```bash
64
+ # With API key flag
65
+ zeph notify --key ak_... --title "Deploy done" --body "v2.1.0 shipped"
66
+
67
+ # With environment variable
68
+ export ZEPH_API_KEY=ak_...
69
+ zeph notify --title "Build failed" --url https://ci.example.com/123
70
+
71
+ # JSON output
72
+ zeph notify --title "Hello" --json
73
+ ```
74
+
75
+ ### CLI Options
76
+
77
+ | Flag | Description |
78
+ |------|-------------|
79
+ | `--key <api-key>` | API key (or set `ZEPH_API_KEY` env) |
80
+ | `--title <text>` | Push title |
81
+ | `--body <text>` | Push body |
82
+ | `--url <url>` | URL to include |
83
+ | `--type <type>` | Push type: `note`, `link`, `file` |
84
+ | `--device <id>` | Target device ID |
85
+ | `--base-url <url>` | API base URL |
86
+ | `--json` | Output JSON format |
87
+ | `--version` | Print version |
88
+
89
+ ### Exit Codes
90
+
91
+ | Code | Meaning |
92
+ |------|---------|
93
+ | 0 | Success |
94
+ | 1 | General error |
95
+ | 2 | Quota exceeded |
96
+ | 3 | Authentication failed |
97
+
98
+ ## Requirements
99
+
100
+ - Node.js >= 18 (uses native `fetch`)
101
+ - Zero runtime dependencies
102
+
103
+ ## License
104
+
105
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const zeph_hook_js_1 = require("./zeph-hook.js");
7
+ const errors_js_1 = require("./errors.js");
8
+ const getVersion = () => {
9
+ try {
10
+ const pkg = JSON.parse((0, fs_1.readFileSync)((0, path_1.join)(__dirname, '..', 'package.json'), 'utf-8'));
11
+ return pkg.version;
12
+ }
13
+ catch {
14
+ return '0.0.0';
15
+ }
16
+ };
17
+ // ── Arg Parser ──────────────────────────────────────────────────
18
+ const parseArgs = (argv) => {
19
+ const result = {};
20
+ const args = argv.slice(2);
21
+ for (let i = 0; i < args.length; i++) {
22
+ const arg = args[i];
23
+ if (!arg.startsWith('--')) {
24
+ if (!result._command)
25
+ result._command = arg;
26
+ continue;
27
+ }
28
+ const key = arg.slice(2);
29
+ const next = args[i + 1];
30
+ if (!next || next.startsWith('--')) {
31
+ result[key] = true;
32
+ }
33
+ else {
34
+ result[key] = next;
35
+ i++;
36
+ }
37
+ }
38
+ return result;
39
+ };
40
+ // ── Output ──────────────────────────────────────────────────────
41
+ const printUsage = () => {
42
+ console.log(`Usage: zeph notify [options]
43
+
44
+ Options:
45
+ --key <api-key> API key (or set ZEPH_API_KEY env)
46
+ --title <text> Push title
47
+ --body <text> Push body
48
+ --url <url> URL to include
49
+ --type <type> Push type (note|link|file) [default: note]
50
+ --device <id> Target device ID
51
+ --base-url <url> API base URL
52
+ --json Output JSON format
53
+
54
+ Environment:
55
+ ZEPH_API_KEY API key (fallback when --key not provided)`);
56
+ };
57
+ const printError = (message, isJson) => {
58
+ if (isJson) {
59
+ console.error(JSON.stringify({ error: message, status: 'error' }));
60
+ }
61
+ else {
62
+ console.error(`Error: ${message}`);
63
+ }
64
+ };
65
+ const printSuccess = (pushId, isJson) => {
66
+ if (isJson) {
67
+ console.log(JSON.stringify({ pushId, status: 'ok' }));
68
+ }
69
+ else {
70
+ console.log(`Push sent: ${pushId}`);
71
+ }
72
+ };
73
+ // ── Main ────────────────────────────────────────────────────────
74
+ const main = async () => {
75
+ const args = parseArgs(process.argv);
76
+ const command = args._command;
77
+ const isJson = args.json === true;
78
+ if (args.version === true) {
79
+ console.log(getVersion());
80
+ return 0;
81
+ }
82
+ if (!command || command === 'help') {
83
+ printUsage();
84
+ return 0;
85
+ }
86
+ if (command !== 'notify') {
87
+ printError(`Unknown command: ${command}`, isJson);
88
+ printUsage();
89
+ return 1;
90
+ }
91
+ const apiKey = args.key || process.env.ZEPH_API_KEY;
92
+ if (!apiKey) {
93
+ printError('API key required. Use --key or set ZEPH_API_KEY', isJson);
94
+ return 3;
95
+ }
96
+ const hook = new zeph_hook_js_1.ZephHook({
97
+ apiKey,
98
+ baseUrl: args['base-url'],
99
+ });
100
+ try {
101
+ const result = await hook.notify({
102
+ title: args.title,
103
+ body: args.body,
104
+ url: args.url,
105
+ type: args.type || undefined,
106
+ targetDeviceId: args.device,
107
+ });
108
+ printSuccess(result.pushId, isJson);
109
+ return 0;
110
+ }
111
+ catch (err) {
112
+ if (err instanceof errors_js_1.QuotaExceededError) {
113
+ printError(err.message, isJson);
114
+ return 2;
115
+ }
116
+ if (err instanceof errors_js_1.AuthenticationError) {
117
+ printError(err.message, isJson);
118
+ return 3;
119
+ }
120
+ if (err instanceof errors_js_1.ZephError) {
121
+ printError(err.message, isJson);
122
+ return 1;
123
+ }
124
+ printError(err instanceof Error ? err.message : 'Unknown error', isJson);
125
+ return 1;
126
+ }
127
+ };
128
+ main().then((code) => process.exit(code));
@@ -0,0 +1,12 @@
1
+ export declare class ZephError extends Error {
2
+ code: string;
3
+ status: number;
4
+ constructor(message: string, code: string, status: number);
5
+ }
6
+ export declare class AuthenticationError extends ZephError {
7
+ constructor(message?: string);
8
+ }
9
+ export declare class QuotaExceededError extends ZephError {
10
+ constructor(message?: string);
11
+ }
12
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,SAAU,SAAQ,KAAK;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;gBAEH,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAM1D;AAED,qBAAa,mBAAoB,SAAQ,SAAS;gBACpC,OAAO,SAA0B;CAI9C;AAED,qBAAa,kBAAmB,SAAQ,SAAS;gBACnC,OAAO,SAAmB;CAIvC"}
package/dist/errors.js ADDED
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QuotaExceededError = exports.AuthenticationError = exports.ZephError = void 0;
4
+ class ZephError extends Error {
5
+ code;
6
+ status;
7
+ constructor(message, code, status) {
8
+ super(message);
9
+ this.name = 'ZephError';
10
+ this.code = code;
11
+ this.status = status;
12
+ }
13
+ }
14
+ exports.ZephError = ZephError;
15
+ class AuthenticationError extends ZephError {
16
+ constructor(message = 'Authentication failed') {
17
+ super(message, 'AUTHENTICATION_ERROR', 401);
18
+ this.name = 'AuthenticationError';
19
+ }
20
+ }
21
+ exports.AuthenticationError = AuthenticationError;
22
+ class QuotaExceededError extends ZephError {
23
+ constructor(message = 'Quota exceeded') {
24
+ super(message, 'QUOTA_EXCEEDED', 403);
25
+ this.name = 'QuotaExceededError';
26
+ }
27
+ }
28
+ exports.QuotaExceededError = QuotaExceededError;
@@ -0,0 +1,4 @@
1
+ export { ZephHook } from './zeph-hook.js';
2
+ export { ZephError, AuthenticationError, QuotaExceededError } from './errors.js';
3
+ export type { ZephOptions, NotifyPayload, NotifyResult } from './types.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjF,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QuotaExceededError = exports.AuthenticationError = exports.ZephError = exports.ZephHook = void 0;
4
+ var zeph_hook_js_1 = require("./zeph-hook.js");
5
+ Object.defineProperty(exports, "ZephHook", { enumerable: true, get: function () { return zeph_hook_js_1.ZephHook; } });
6
+ var errors_js_1 = require("./errors.js");
7
+ Object.defineProperty(exports, "ZephError", { enumerable: true, get: function () { return errors_js_1.ZephError; } });
8
+ Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return errors_js_1.AuthenticationError; } });
9
+ Object.defineProperty(exports, "QuotaExceededError", { enumerable: true, get: function () { return errors_js_1.QuotaExceededError; } });
@@ -0,0 +1,23 @@
1
+ export interface ZephOptions {
2
+ apiKey: string;
3
+ baseUrl?: string;
4
+ timeout?: number;
5
+ }
6
+ export interface NotifyPayload {
7
+ title?: string;
8
+ body?: string;
9
+ url?: string;
10
+ type?: 'note' | 'link' | 'file';
11
+ targetDeviceId?: string;
12
+ }
13
+ export interface NotifyResult {
14
+ pushId: string;
15
+ }
16
+ export interface ApiErrorResponse {
17
+ error: {
18
+ code: string;
19
+ message: string;
20
+ status: number;
21
+ };
22
+ }
23
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,10 @@
1
+ import type { ZephOptions, NotifyPayload, NotifyResult } from './types.js';
2
+ export declare class ZephHook {
3
+ private readonly apiKey;
4
+ private readonly baseUrl;
5
+ private readonly timeoutMs;
6
+ constructor(options: ZephOptions);
7
+ notify(payload: NotifyPayload): Promise<NotifyResult>;
8
+ private parseError;
9
+ }
10
+ //# sourceMappingURL=zeph-hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zeph-hook.d.ts","sourceRoot":"","sources":["../src/zeph-hook.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAoB,MAAM,YAAY,CAAC;AAM7F,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,WAAW;IAS1B,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAqC3D,OAAO,CAAC,UAAU;CASnB"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ZephHook = void 0;
4
+ const errors_js_1 = require("./errors.js");
5
+ const DEFAULT_BASE_URL = 'https://api.zeph.to';
6
+ const DEFAULT_TIMEOUT_MS = 30_000;
7
+ class ZephHook {
8
+ apiKey;
9
+ baseUrl;
10
+ timeoutMs;
11
+ constructor(options) {
12
+ if (!options.apiKey) {
13
+ throw new errors_js_1.ZephError('apiKey is required', 'INVALID_OPTIONS', 400);
14
+ }
15
+ this.apiKey = options.apiKey;
16
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, '');
17
+ this.timeoutMs = options.timeout ?? DEFAULT_TIMEOUT_MS;
18
+ }
19
+ async notify(payload) {
20
+ const controller = new AbortController();
21
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
22
+ let response;
23
+ try {
24
+ response = await fetch(`${this.baseUrl}/v1/pushes/send`, {
25
+ method: 'POST',
26
+ headers: {
27
+ 'Content-Type': 'application/json',
28
+ 'X-API-Key': this.apiKey,
29
+ },
30
+ body: JSON.stringify(payload),
31
+ signal: controller.signal,
32
+ });
33
+ }
34
+ catch (err) {
35
+ if (err instanceof DOMException && err.name === 'AbortError') {
36
+ throw new errors_js_1.ZephError(`Request timed out after ${this.timeoutMs}ms`, 'TIMEOUT', 408);
37
+ }
38
+ throw err;
39
+ }
40
+ finally {
41
+ clearTimeout(timer);
42
+ }
43
+ const json = await response.json();
44
+ if (!response.ok) {
45
+ throw this.parseError(response.status, json);
46
+ }
47
+ const pushId = json.data?.pushId;
48
+ if (!pushId) {
49
+ throw new errors_js_1.ZephError('Server returned no pushId', 'INVALID_RESPONSE', response.status);
50
+ }
51
+ return { pushId };
52
+ }
53
+ parseError(status, body) {
54
+ const message = body.error?.message ?? `Request failed with status ${status}`;
55
+ const code = body.error?.code ?? 'UNKNOWN_ERROR';
56
+ if (status === 401)
57
+ return new errors_js_1.AuthenticationError(message);
58
+ if (status === 403 && code === 'QUOTA_EXCEEDED')
59
+ return new errors_js_1.QuotaExceededError(message);
60
+ return new errors_js_1.ZephError(message, code, status);
61
+ }
62
+ }
63
+ exports.ZephHook = ZephHook;
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@zeph-to/hook-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Zeph push notification SDK + CLI — zero dependencies",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "bin": {
14
+ "zeph": "./dist/cli.js"
15
+ },
16
+ "engines": {
17
+ "node": ">=18.0.0"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "!dist/**/*.tsbuildinfo"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsc -p tsconfig.lib.json",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/tak-bro/encl",
30
+ "directory": "libs/hook-sdk"
31
+ },
32
+ "homepage": "https://github.com/tak-bro/encl/tree/main/libs/hook-sdk",
33
+ "keywords": ["zeph", "push", "notification", "cli", "webhook"],
34
+ "license": "MIT"
35
+ }