@emmanuel-nike/ark-notify-js 0.1.0 → 0.1.2

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.
@@ -0,0 +1,128 @@
1
+ 'use strict';
2
+
3
+ var crypto = require('crypto');
4
+
5
+ // src/server-auth.ts
6
+ var CLIENT_ID_PATTERN = /^[a-zA-Z0-9_\-.]{1,128}$/;
7
+ var DEFAULT_TTL_SECONDS = 3600;
8
+ var MAX_TTL_SECONDS = 86400;
9
+ var DEFAULT_CAPABILITIES = {
10
+ subscribe: true,
11
+ publish: true,
12
+ presence: true
13
+ };
14
+ function isValidClientId(clientId) {
15
+ return typeof clientId === "string" && CLIENT_ID_PATTERN.test(clientId);
16
+ }
17
+ function normalizeCapabilities(capabilities) {
18
+ if (!capabilities || typeof capabilities !== "object") {
19
+ return { ...DEFAULT_CAPABILITIES };
20
+ }
21
+ return {
22
+ subscribe: capabilities.subscribe !== false,
23
+ publish: capabilities.publish !== false,
24
+ presence: capabilities.presence !== false
25
+ };
26
+ }
27
+ function resolveTokenTtl(ttl) {
28
+ if (ttl === void 0 || ttl === null) {
29
+ return DEFAULT_TTL_SECONDS;
30
+ }
31
+ if (!Number.isInteger(ttl) || ttl <= 0) {
32
+ throw new Error("ttl must be a positive integer (seconds)");
33
+ }
34
+ return Math.min(ttl, MAX_TTL_SECONDS);
35
+ }
36
+ function encodePayload(payload) {
37
+ return Buffer.from(JSON.stringify(payload)).toString("base64url");
38
+ }
39
+ function computeConnectionTokenSignature(secret, appKey, payloadEncoded) {
40
+ return crypto.createHmac("sha256", secret).update(`${appKey}.${payloadEncoded}`).digest("hex");
41
+ }
42
+ function buildConnectionToken(appKey, secret, options) {
43
+ if (!isValidClientId(options.clientId)) {
44
+ throw new Error("clientId must be 1-128 alphanumeric characters");
45
+ }
46
+ const payloadEncoded = encodePayload({
47
+ client_id: options.clientId,
48
+ exp: options.exp,
49
+ capabilities: normalizeCapabilities(options.capabilities)
50
+ });
51
+ const signature = computeConnectionTokenSignature(secret, appKey, payloadEncoded);
52
+ return `${appKey}.${payloadEncoded}.${signature}`;
53
+ }
54
+ function parseServerAuthRequest(body) {
55
+ if (!body || typeof body !== "object") {
56
+ return null;
57
+ }
58
+ const record = body;
59
+ const client_id = record.client_id ?? record.clientId;
60
+ if (!isValidClientId(client_id)) {
61
+ return null;
62
+ }
63
+ const user_data = record.user_data ?? record.userData;
64
+ const capabilities = record.capabilities;
65
+ const ttl = record.ttl;
66
+ return {
67
+ client_id,
68
+ user_data: user_data === void 0 ? null : user_data && typeof user_data === "object" ? user_data : null,
69
+ capabilities: capabilities && typeof capabilities === "object" ? capabilities : null,
70
+ ttl: typeof ttl === "number" ? ttl : null
71
+ };
72
+ }
73
+ function createAuthorizedServerAuthResponse(options) {
74
+ if (!isValidClientId(options.clientId)) {
75
+ throw new Error("clientId must be 1-128 alphanumeric characters");
76
+ }
77
+ const capabilities = normalizeCapabilities(options.capabilities);
78
+ const ttl = resolveTokenTtl(options.ttl);
79
+ if (options.credentials) {
80
+ const exp = Math.floor(Date.now() / 1e3) + ttl;
81
+ return {
82
+ token: buildConnectionToken(options.credentials.appKey, options.credentials.secret, {
83
+ clientId: options.clientId,
84
+ exp,
85
+ capabilities
86
+ })
87
+ };
88
+ }
89
+ return {
90
+ allowed: true,
91
+ client_id: options.clientId,
92
+ capabilities,
93
+ ttl
94
+ };
95
+ }
96
+ function createDeniedServerAuthResponse(reason) {
97
+ return reason ? { allowed: false, reason } : { allowed: false };
98
+ }
99
+ async function handleServerAuth(options) {
100
+ const decision = await options.isAuthorized(options.request);
101
+ if (decision === false) {
102
+ return createDeniedServerAuthResponse();
103
+ }
104
+ const clientId = decision.clientId ?? options.request.client_id;
105
+ if (!isValidClientId(clientId)) {
106
+ return createDeniedServerAuthResponse("invalid_client_id");
107
+ }
108
+ try {
109
+ return createAuthorizedServerAuthResponse({
110
+ clientId,
111
+ capabilities: decision.capabilities ?? options.request.capabilities ?? void 0,
112
+ ttl: decision.ttl ?? options.request.ttl ?? void 0,
113
+ credentials: options.credentials
114
+ });
115
+ } catch (err) {
116
+ const message = err instanceof Error ? err.message : "invalid_request";
117
+ return createDeniedServerAuthResponse(message);
118
+ }
119
+ }
120
+
121
+ exports.buildConnectionToken = buildConnectionToken;
122
+ exports.createAuthorizedServerAuthResponse = createAuthorizedServerAuthResponse;
123
+ exports.createDeniedServerAuthResponse = createDeniedServerAuthResponse;
124
+ exports.handleServerAuth = handleServerAuth;
125
+ exports.isValidClientId = isValidClientId;
126
+ exports.parseServerAuthRequest = parseServerAuthRequest;
127
+ //# sourceMappingURL=index.cjs.map
128
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server-auth.ts"],"names":["createHmac"],"mappings":"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","file":"index.cjs","sourcesContent":["import { createHmac } from 'node:crypto'\nimport type {\n ConnectionCapabilities,\n CreateAuthorizedServerAuthResponseOptions,\n HandleServerAuthOptions,\n ServerAuthApprovedResponse,\n ServerAuthDeniedResponse,\n ServerAuthRequest,\n ServerAuthResponse,\n ServerAuthDecision,\n} from './types'\n\nconst CLIENT_ID_PATTERN = /^[a-zA-Z0-9_\\-.]{1,128}$/\nconst DEFAULT_TTL_SECONDS = 3600\nconst MAX_TTL_SECONDS = 86400\n\nconst DEFAULT_CAPABILITIES: ConnectionCapabilities = {\n subscribe: true,\n publish: true,\n presence: true,\n}\n\nexport function isValidClientId(clientId: unknown): clientId is string {\n return typeof clientId === 'string' && CLIENT_ID_PATTERN.test(clientId)\n}\n\nfunction normalizeCapabilities(\n capabilities?: Partial<ConnectionCapabilities> | null\n): ConnectionCapabilities {\n if (!capabilities || typeof capabilities !== 'object') {\n return { ...DEFAULT_CAPABILITIES }\n }\n\n return {\n subscribe: capabilities.subscribe !== false,\n publish: capabilities.publish !== false,\n presence: capabilities.presence !== false,\n }\n}\n\nfunction resolveTokenTtl(ttl?: number | null): number {\n if (ttl === undefined || ttl === null) {\n return DEFAULT_TTL_SECONDS\n }\n\n if (!Number.isInteger(ttl) || ttl <= 0) {\n throw new Error('ttl must be a positive integer (seconds)')\n }\n\n return Math.min(ttl, MAX_TTL_SECONDS)\n}\n\nfunction encodePayload(payload: Record<string, unknown>): string {\n return Buffer.from(JSON.stringify(payload)).toString('base64url')\n}\n\nfunction computeConnectionTokenSignature(\n secret: string,\n appKey: string,\n payloadEncoded: string\n): string {\n return createHmac('sha256', secret).update(`${appKey}.${payloadEncoded}`).digest('hex')\n}\n\n/** Build a signed connection token in Ark Notify format. */\nexport function buildConnectionToken(\n appKey: string,\n secret: string,\n options: {\n clientId: string\n exp: number\n capabilities?: Partial<ConnectionCapabilities>\n }\n): string {\n if (!isValidClientId(options.clientId)) {\n throw new Error('clientId must be 1-128 alphanumeric characters')\n }\n\n const payloadEncoded = encodePayload({\n client_id: options.clientId,\n exp: options.exp,\n capabilities: normalizeCapabilities(options.capabilities),\n })\n\n const signature = computeConnectionTokenSignature(secret, appKey, payloadEncoded)\n return `${appKey}.${payloadEncoded}.${signature}`\n}\n\n/**\n * Parse the JSON body Ark Notify sends to your `serverAuthUrl`.\n * Returns null when the payload is invalid.\n */\nexport function parseServerAuthRequest(body: unknown): ServerAuthRequest | null {\n if (!body || typeof body !== 'object') {\n return null\n }\n\n const record = body as Record<string, unknown>\n const client_id = record.client_id ?? record.clientId\n\n if (!isValidClientId(client_id)) {\n return null\n }\n\n const user_data = record.user_data ?? record.userData\n const capabilities = record.capabilities\n const ttl = record.ttl\n\n return {\n client_id,\n user_data:\n user_data === undefined\n ? null\n : user_data && typeof user_data === 'object'\n ? (user_data as Record<string, unknown>)\n : null,\n capabilities:\n capabilities && typeof capabilities === 'object'\n ? (capabilities as Partial<ConnectionCapabilities>)\n : null,\n ttl: typeof ttl === 'number' ? ttl : null,\n }\n}\n\n/**\n * Build an approved response for your `serverAuthUrl` webhook.\n *\n * - Without `credentials`: returns `{ allowed: true, client_id, ... }` (option A).\n * - With `credentials`: returns `{ token }` with a pre-signed token (option B).\n */\nexport function createAuthorizedServerAuthResponse(\n options: CreateAuthorizedServerAuthResponseOptions\n): ServerAuthApprovedResponse {\n if (!isValidClientId(options.clientId)) {\n throw new Error('clientId must be 1-128 alphanumeric characters')\n }\n\n const capabilities = normalizeCapabilities(options.capabilities)\n const ttl = resolveTokenTtl(options.ttl)\n\n if (options.credentials) {\n const exp = Math.floor(Date.now() / 1000) + ttl\n return {\n token: buildConnectionToken(options.credentials.appKey, options.credentials.secret, {\n clientId: options.clientId,\n exp,\n capabilities,\n }),\n }\n }\n\n return {\n allowed: true,\n client_id: options.clientId,\n capabilities,\n ttl,\n }\n}\n\n/** Build a denied response for your `serverAuthUrl` webhook. */\nexport function createDeniedServerAuthResponse(reason?: string): ServerAuthDeniedResponse {\n return reason ? { allowed: false, reason } : { allowed: false }\n}\n\n/**\n * Authenticate an incoming `serverAuthUrl` request and return the webhook response\n * body Ark Notify expects.\n */\nexport async function handleServerAuth(\n options: HandleServerAuthOptions\n): Promise<ServerAuthResponse> {\n const decision: ServerAuthDecision = await options.isAuthorized(options.request)\n\n if (decision === false) {\n return createDeniedServerAuthResponse()\n }\n\n const clientId = decision.clientId ?? options.request.client_id\n if (!isValidClientId(clientId)) {\n return createDeniedServerAuthResponse('invalid_client_id')\n }\n\n try {\n return createAuthorizedServerAuthResponse({\n clientId,\n capabilities: decision.capabilities ?? options.request.capabilities ?? undefined,\n ttl: decision.ttl ?? options.request.ttl ?? undefined,\n credentials: options.credentials,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : 'invalid_request'\n return createDeniedServerAuthResponse(message)\n }\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import { m as ConnectionCapabilities, p as CreateAuthorizedServerAuthResponseOptions, D as ServerAuthApprovedResponse, G as ServerAuthDeniedResponse, H as HandleServerAuthOptions, J as ServerAuthResponse, I as ServerAuthRequest } from '../types-tQECeZab.cjs';
2
+ export { S as ServerAuthAllowedResponse, F as ServerAuthDecision, K as ServerAuthTokenResponse } from '../types-tQECeZab.cjs';
3
+
4
+ declare function isValidClientId(clientId: unknown): clientId is string;
5
+ /** Build a signed connection token in Ark Notify format. */
6
+ declare function buildConnectionToken(appKey: string, secret: string, options: {
7
+ clientId: string;
8
+ exp: number;
9
+ capabilities?: Partial<ConnectionCapabilities>;
10
+ }): string;
11
+ /**
12
+ * Parse the JSON body Ark Notify sends to your `serverAuthUrl`.
13
+ * Returns null when the payload is invalid.
14
+ */
15
+ declare function parseServerAuthRequest(body: unknown): ServerAuthRequest | null;
16
+ /**
17
+ * Build an approved response for your `serverAuthUrl` webhook.
18
+ *
19
+ * - Without `credentials`: returns `{ allowed: true, client_id, ... }` (option A).
20
+ * - With `credentials`: returns `{ token }` with a pre-signed token (option B).
21
+ */
22
+ declare function createAuthorizedServerAuthResponse(options: CreateAuthorizedServerAuthResponseOptions): ServerAuthApprovedResponse;
23
+ /** Build a denied response for your `serverAuthUrl` webhook. */
24
+ declare function createDeniedServerAuthResponse(reason?: string): ServerAuthDeniedResponse;
25
+ /**
26
+ * Authenticate an incoming `serverAuthUrl` request and return the webhook response
27
+ * body Ark Notify expects.
28
+ */
29
+ declare function handleServerAuth(options: HandleServerAuthOptions): Promise<ServerAuthResponse>;
30
+
31
+ export { CreateAuthorizedServerAuthResponseOptions, HandleServerAuthOptions, ServerAuthApprovedResponse, ServerAuthDeniedResponse, ServerAuthRequest, ServerAuthResponse, buildConnectionToken, createAuthorizedServerAuthResponse, createDeniedServerAuthResponse, handleServerAuth, isValidClientId, parseServerAuthRequest };
@@ -0,0 +1,31 @@
1
+ import { m as ConnectionCapabilities, p as CreateAuthorizedServerAuthResponseOptions, D as ServerAuthApprovedResponse, G as ServerAuthDeniedResponse, H as HandleServerAuthOptions, J as ServerAuthResponse, I as ServerAuthRequest } from '../types-tQECeZab.js';
2
+ export { S as ServerAuthAllowedResponse, F as ServerAuthDecision, K as ServerAuthTokenResponse } from '../types-tQECeZab.js';
3
+
4
+ declare function isValidClientId(clientId: unknown): clientId is string;
5
+ /** Build a signed connection token in Ark Notify format. */
6
+ declare function buildConnectionToken(appKey: string, secret: string, options: {
7
+ clientId: string;
8
+ exp: number;
9
+ capabilities?: Partial<ConnectionCapabilities>;
10
+ }): string;
11
+ /**
12
+ * Parse the JSON body Ark Notify sends to your `serverAuthUrl`.
13
+ * Returns null when the payload is invalid.
14
+ */
15
+ declare function parseServerAuthRequest(body: unknown): ServerAuthRequest | null;
16
+ /**
17
+ * Build an approved response for your `serverAuthUrl` webhook.
18
+ *
19
+ * - Without `credentials`: returns `{ allowed: true, client_id, ... }` (option A).
20
+ * - With `credentials`: returns `{ token }` with a pre-signed token (option B).
21
+ */
22
+ declare function createAuthorizedServerAuthResponse(options: CreateAuthorizedServerAuthResponseOptions): ServerAuthApprovedResponse;
23
+ /** Build a denied response for your `serverAuthUrl` webhook. */
24
+ declare function createDeniedServerAuthResponse(reason?: string): ServerAuthDeniedResponse;
25
+ /**
26
+ * Authenticate an incoming `serverAuthUrl` request and return the webhook response
27
+ * body Ark Notify expects.
28
+ */
29
+ declare function handleServerAuth(options: HandleServerAuthOptions): Promise<ServerAuthResponse>;
30
+
31
+ export { CreateAuthorizedServerAuthResponseOptions, HandleServerAuthOptions, ServerAuthApprovedResponse, ServerAuthDeniedResponse, ServerAuthRequest, ServerAuthResponse, buildConnectionToken, createAuthorizedServerAuthResponse, createDeniedServerAuthResponse, handleServerAuth, isValidClientId, parseServerAuthRequest };
@@ -0,0 +1,121 @@
1
+ import { createHmac } from 'crypto';
2
+
3
+ // src/server-auth.ts
4
+ var CLIENT_ID_PATTERN = /^[a-zA-Z0-9_\-.]{1,128}$/;
5
+ var DEFAULT_TTL_SECONDS = 3600;
6
+ var MAX_TTL_SECONDS = 86400;
7
+ var DEFAULT_CAPABILITIES = {
8
+ subscribe: true,
9
+ publish: true,
10
+ presence: true
11
+ };
12
+ function isValidClientId(clientId) {
13
+ return typeof clientId === "string" && CLIENT_ID_PATTERN.test(clientId);
14
+ }
15
+ function normalizeCapabilities(capabilities) {
16
+ if (!capabilities || typeof capabilities !== "object") {
17
+ return { ...DEFAULT_CAPABILITIES };
18
+ }
19
+ return {
20
+ subscribe: capabilities.subscribe !== false,
21
+ publish: capabilities.publish !== false,
22
+ presence: capabilities.presence !== false
23
+ };
24
+ }
25
+ function resolveTokenTtl(ttl) {
26
+ if (ttl === void 0 || ttl === null) {
27
+ return DEFAULT_TTL_SECONDS;
28
+ }
29
+ if (!Number.isInteger(ttl) || ttl <= 0) {
30
+ throw new Error("ttl must be a positive integer (seconds)");
31
+ }
32
+ return Math.min(ttl, MAX_TTL_SECONDS);
33
+ }
34
+ function encodePayload(payload) {
35
+ return Buffer.from(JSON.stringify(payload)).toString("base64url");
36
+ }
37
+ function computeConnectionTokenSignature(secret, appKey, payloadEncoded) {
38
+ return createHmac("sha256", secret).update(`${appKey}.${payloadEncoded}`).digest("hex");
39
+ }
40
+ function buildConnectionToken(appKey, secret, options) {
41
+ if (!isValidClientId(options.clientId)) {
42
+ throw new Error("clientId must be 1-128 alphanumeric characters");
43
+ }
44
+ const payloadEncoded = encodePayload({
45
+ client_id: options.clientId,
46
+ exp: options.exp,
47
+ capabilities: normalizeCapabilities(options.capabilities)
48
+ });
49
+ const signature = computeConnectionTokenSignature(secret, appKey, payloadEncoded);
50
+ return `${appKey}.${payloadEncoded}.${signature}`;
51
+ }
52
+ function parseServerAuthRequest(body) {
53
+ if (!body || typeof body !== "object") {
54
+ return null;
55
+ }
56
+ const record = body;
57
+ const client_id = record.client_id ?? record.clientId;
58
+ if (!isValidClientId(client_id)) {
59
+ return null;
60
+ }
61
+ const user_data = record.user_data ?? record.userData;
62
+ const capabilities = record.capabilities;
63
+ const ttl = record.ttl;
64
+ return {
65
+ client_id,
66
+ user_data: user_data === void 0 ? null : user_data && typeof user_data === "object" ? user_data : null,
67
+ capabilities: capabilities && typeof capabilities === "object" ? capabilities : null,
68
+ ttl: typeof ttl === "number" ? ttl : null
69
+ };
70
+ }
71
+ function createAuthorizedServerAuthResponse(options) {
72
+ if (!isValidClientId(options.clientId)) {
73
+ throw new Error("clientId must be 1-128 alphanumeric characters");
74
+ }
75
+ const capabilities = normalizeCapabilities(options.capabilities);
76
+ const ttl = resolveTokenTtl(options.ttl);
77
+ if (options.credentials) {
78
+ const exp = Math.floor(Date.now() / 1e3) + ttl;
79
+ return {
80
+ token: buildConnectionToken(options.credentials.appKey, options.credentials.secret, {
81
+ clientId: options.clientId,
82
+ exp,
83
+ capabilities
84
+ })
85
+ };
86
+ }
87
+ return {
88
+ allowed: true,
89
+ client_id: options.clientId,
90
+ capabilities,
91
+ ttl
92
+ };
93
+ }
94
+ function createDeniedServerAuthResponse(reason) {
95
+ return reason ? { allowed: false, reason } : { allowed: false };
96
+ }
97
+ async function handleServerAuth(options) {
98
+ const decision = await options.isAuthorized(options.request);
99
+ if (decision === false) {
100
+ return createDeniedServerAuthResponse();
101
+ }
102
+ const clientId = decision.clientId ?? options.request.client_id;
103
+ if (!isValidClientId(clientId)) {
104
+ return createDeniedServerAuthResponse("invalid_client_id");
105
+ }
106
+ try {
107
+ return createAuthorizedServerAuthResponse({
108
+ clientId,
109
+ capabilities: decision.capabilities ?? options.request.capabilities ?? void 0,
110
+ ttl: decision.ttl ?? options.request.ttl ?? void 0,
111
+ credentials: options.credentials
112
+ });
113
+ } catch (err) {
114
+ const message = err instanceof Error ? err.message : "invalid_request";
115
+ return createDeniedServerAuthResponse(message);
116
+ }
117
+ }
118
+
119
+ export { buildConnectionToken, createAuthorizedServerAuthResponse, createDeniedServerAuthResponse, handleServerAuth, isValidClientId, parseServerAuthRequest };
120
+ //# sourceMappingURL=index.js.map
121
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server-auth.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { createHmac } from 'node:crypto'\nimport type {\n ConnectionCapabilities,\n CreateAuthorizedServerAuthResponseOptions,\n HandleServerAuthOptions,\n ServerAuthApprovedResponse,\n ServerAuthDeniedResponse,\n ServerAuthRequest,\n ServerAuthResponse,\n ServerAuthDecision,\n} from './types'\n\nconst CLIENT_ID_PATTERN = /^[a-zA-Z0-9_\\-.]{1,128}$/\nconst DEFAULT_TTL_SECONDS = 3600\nconst MAX_TTL_SECONDS = 86400\n\nconst DEFAULT_CAPABILITIES: ConnectionCapabilities = {\n subscribe: true,\n publish: true,\n presence: true,\n}\n\nexport function isValidClientId(clientId: unknown): clientId is string {\n return typeof clientId === 'string' && CLIENT_ID_PATTERN.test(clientId)\n}\n\nfunction normalizeCapabilities(\n capabilities?: Partial<ConnectionCapabilities> | null\n): ConnectionCapabilities {\n if (!capabilities || typeof capabilities !== 'object') {\n return { ...DEFAULT_CAPABILITIES }\n }\n\n return {\n subscribe: capabilities.subscribe !== false,\n publish: capabilities.publish !== false,\n presence: capabilities.presence !== false,\n }\n}\n\nfunction resolveTokenTtl(ttl?: number | null): number {\n if (ttl === undefined || ttl === null) {\n return DEFAULT_TTL_SECONDS\n }\n\n if (!Number.isInteger(ttl) || ttl <= 0) {\n throw new Error('ttl must be a positive integer (seconds)')\n }\n\n return Math.min(ttl, MAX_TTL_SECONDS)\n}\n\nfunction encodePayload(payload: Record<string, unknown>): string {\n return Buffer.from(JSON.stringify(payload)).toString('base64url')\n}\n\nfunction computeConnectionTokenSignature(\n secret: string,\n appKey: string,\n payloadEncoded: string\n): string {\n return createHmac('sha256', secret).update(`${appKey}.${payloadEncoded}`).digest('hex')\n}\n\n/** Build a signed connection token in Ark Notify format. */\nexport function buildConnectionToken(\n appKey: string,\n secret: string,\n options: {\n clientId: string\n exp: number\n capabilities?: Partial<ConnectionCapabilities>\n }\n): string {\n if (!isValidClientId(options.clientId)) {\n throw new Error('clientId must be 1-128 alphanumeric characters')\n }\n\n const payloadEncoded = encodePayload({\n client_id: options.clientId,\n exp: options.exp,\n capabilities: normalizeCapabilities(options.capabilities),\n })\n\n const signature = computeConnectionTokenSignature(secret, appKey, payloadEncoded)\n return `${appKey}.${payloadEncoded}.${signature}`\n}\n\n/**\n * Parse the JSON body Ark Notify sends to your `serverAuthUrl`.\n * Returns null when the payload is invalid.\n */\nexport function parseServerAuthRequest(body: unknown): ServerAuthRequest | null {\n if (!body || typeof body !== 'object') {\n return null\n }\n\n const record = body as Record<string, unknown>\n const client_id = record.client_id ?? record.clientId\n\n if (!isValidClientId(client_id)) {\n return null\n }\n\n const user_data = record.user_data ?? record.userData\n const capabilities = record.capabilities\n const ttl = record.ttl\n\n return {\n client_id,\n user_data:\n user_data === undefined\n ? null\n : user_data && typeof user_data === 'object'\n ? (user_data as Record<string, unknown>)\n : null,\n capabilities:\n capabilities && typeof capabilities === 'object'\n ? (capabilities as Partial<ConnectionCapabilities>)\n : null,\n ttl: typeof ttl === 'number' ? ttl : null,\n }\n}\n\n/**\n * Build an approved response for your `serverAuthUrl` webhook.\n *\n * - Without `credentials`: returns `{ allowed: true, client_id, ... }` (option A).\n * - With `credentials`: returns `{ token }` with a pre-signed token (option B).\n */\nexport function createAuthorizedServerAuthResponse(\n options: CreateAuthorizedServerAuthResponseOptions\n): ServerAuthApprovedResponse {\n if (!isValidClientId(options.clientId)) {\n throw new Error('clientId must be 1-128 alphanumeric characters')\n }\n\n const capabilities = normalizeCapabilities(options.capabilities)\n const ttl = resolveTokenTtl(options.ttl)\n\n if (options.credentials) {\n const exp = Math.floor(Date.now() / 1000) + ttl\n return {\n token: buildConnectionToken(options.credentials.appKey, options.credentials.secret, {\n clientId: options.clientId,\n exp,\n capabilities,\n }),\n }\n }\n\n return {\n allowed: true,\n client_id: options.clientId,\n capabilities,\n ttl,\n }\n}\n\n/** Build a denied response for your `serverAuthUrl` webhook. */\nexport function createDeniedServerAuthResponse(reason?: string): ServerAuthDeniedResponse {\n return reason ? { allowed: false, reason } : { allowed: false }\n}\n\n/**\n * Authenticate an incoming `serverAuthUrl` request and return the webhook response\n * body Ark Notify expects.\n */\nexport async function handleServerAuth(\n options: HandleServerAuthOptions\n): Promise<ServerAuthResponse> {\n const decision: ServerAuthDecision = await options.isAuthorized(options.request)\n\n if (decision === false) {\n return createDeniedServerAuthResponse()\n }\n\n const clientId = decision.clientId ?? options.request.client_id\n if (!isValidClientId(clientId)) {\n return createDeniedServerAuthResponse('invalid_client_id')\n }\n\n try {\n return createAuthorizedServerAuthResponse({\n clientId,\n capabilities: decision.capabilities ?? options.request.capabilities ?? undefined,\n ttl: decision.ttl ?? options.request.ttl ?? undefined,\n credentials: options.credentials,\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : 'invalid_request'\n return createDeniedServerAuthResponse(message)\n }\n}\n"]}
@@ -0,0 +1,257 @@
1
+ type UserRole = 'SYSTEM_ADMIN' | 'ACCOUNT_ADMIN' | 'ACCOUNT_USER';
2
+ interface User {
3
+ id: string;
4
+ email: string;
5
+ firstName: string;
6
+ lastName: string;
7
+ role: UserRole;
8
+ createdAt: string;
9
+ }
10
+ interface ApiError {
11
+ error: string;
12
+ message: string;
13
+ reason?: string;
14
+ retryAfterSec?: number;
15
+ }
16
+ interface ConnectionCapabilities {
17
+ subscribe: boolean;
18
+ publish: boolean;
19
+ presence: boolean;
20
+ }
21
+ interface Application {
22
+ id: string;
23
+ name: string;
24
+ appKey: string;
25
+ tenantId: string;
26
+ authWebhookUrl: string | null;
27
+ requireClientAuth: boolean;
28
+ serverAuthUrl: string | null;
29
+ messageHistorySize: number;
30
+ createdAt: string;
31
+ updatedAt: string;
32
+ secret?: string;
33
+ }
34
+ interface CreateApplicationInput {
35
+ name: string;
36
+ authWebhookUrl?: string | null;
37
+ requireClientAuth?: boolean;
38
+ serverAuthUrl?: string | null;
39
+ messageHistorySize?: number;
40
+ }
41
+ interface UpdateApplicationInput {
42
+ name?: string;
43
+ authWebhookUrl?: string | null;
44
+ requireClientAuth?: boolean;
45
+ serverAuthUrl?: string | null;
46
+ messageHistorySize?: number;
47
+ }
48
+ interface LoginInput {
49
+ email: string;
50
+ password: string;
51
+ }
52
+ interface AuthResponse {
53
+ user: User;
54
+ token: string;
55
+ }
56
+ interface PublishEventInput {
57
+ channel: string;
58
+ event: string;
59
+ data?: unknown;
60
+ }
61
+ interface PublishEventResponse {
62
+ published: boolean;
63
+ channel: string;
64
+ event: string;
65
+ }
66
+ interface ChannelAuthInput {
67
+ socket_id?: string;
68
+ connection_id?: string;
69
+ channel_name: string;
70
+ user_data?: Record<string, unknown>;
71
+ }
72
+ interface ChannelAuthResponse {
73
+ auth: string;
74
+ }
75
+ interface ConnectionTokenInput {
76
+ client_id?: string;
77
+ clientId?: string;
78
+ user_data?: Record<string, unknown>;
79
+ userData?: Record<string, unknown>;
80
+ serverAuthUrl?: string | null;
81
+ server_auth_url?: string | null;
82
+ ttl?: number;
83
+ capabilities?: Partial<ConnectionCapabilities>;
84
+ }
85
+ interface ConnectionTokenResponse {
86
+ token: string;
87
+ client_id: string;
88
+ expires_at: number;
89
+ capabilities: ConnectionCapabilities;
90
+ }
91
+ /** Payload Ark Notify POSTs to your `serverAuthUrl`. */
92
+ interface ServerAuthRequest {
93
+ client_id: string;
94
+ user_data?: Record<string, unknown> | null;
95
+ capabilities?: Partial<ConnectionCapabilities> | null;
96
+ ttl?: number | null;
97
+ }
98
+ /** Approve a connection token request (option A). */
99
+ interface ServerAuthAllowedResponse {
100
+ allowed: true;
101
+ client_id: string;
102
+ capabilities?: ConnectionCapabilities;
103
+ ttl?: number;
104
+ }
105
+ /** Approve with a pre-signed token (option B). */
106
+ interface ServerAuthTokenResponse {
107
+ token: string;
108
+ }
109
+ type ServerAuthApprovedResponse = ServerAuthAllowedResponse | ServerAuthTokenResponse;
110
+ interface ServerAuthDeniedResponse {
111
+ allowed: false;
112
+ reason?: string;
113
+ }
114
+ type ServerAuthResponse = ServerAuthApprovedResponse | ServerAuthDeniedResponse;
115
+ interface CreateAuthorizedServerAuthResponseOptions {
116
+ clientId: string;
117
+ capabilities?: Partial<ConnectionCapabilities>;
118
+ ttl?: number;
119
+ /** When provided, returns a pre-signed token instead of `{ allowed: true }`. */
120
+ credentials?: AppCredentials;
121
+ }
122
+ type ServerAuthDecision = false | {
123
+ clientId?: string;
124
+ capabilities?: Partial<ConnectionCapabilities>;
125
+ ttl?: number;
126
+ };
127
+ interface HandleServerAuthOptions {
128
+ request: ServerAuthRequest;
129
+ isAuthorized: (request: ServerAuthRequest) => ServerAuthDecision | Promise<ServerAuthDecision>;
130
+ credentials?: AppCredentials;
131
+ }
132
+ type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'failed';
133
+ interface ConnectedMessage {
134
+ type: 'connected';
135
+ connection_id: string;
136
+ client_id: string;
137
+ app_key: string;
138
+ authenticated: boolean;
139
+ channels?: string[];
140
+ }
141
+ interface EventMessage {
142
+ type: 'event';
143
+ channel: string;
144
+ event: string;
145
+ data: unknown;
146
+ clientId: string | null;
147
+ timestamp: number;
148
+ }
149
+ interface PresenceMemberInfo {
150
+ connectionId: string;
151
+ clientId: string;
152
+ data: Record<string, unknown>;
153
+ updatedAt: number;
154
+ }
155
+ type PresenceAction = 'enter' | 'leave' | 'update' | 'sync';
156
+ interface PresenceMessage {
157
+ type: 'presence';
158
+ channel: string;
159
+ action: PresenceAction;
160
+ member: PresenceMemberInfo | null;
161
+ members: PresenceMemberInfo[] | null;
162
+ timestamp: number;
163
+ }
164
+ interface SubscribedMessage {
165
+ type: 'subscribed';
166
+ channel: string;
167
+ }
168
+ interface UnsubscribedMessage {
169
+ type: 'unsubscribed';
170
+ channel: string;
171
+ }
172
+ interface PublishedMessage {
173
+ type: 'published';
174
+ channel: string;
175
+ event: string;
176
+ }
177
+ interface PongMessage {
178
+ type: 'pong';
179
+ timestamp: number;
180
+ }
181
+ interface PingMessage {
182
+ type: 'ping';
183
+ }
184
+ interface ServerErrorMessage {
185
+ type: 'error';
186
+ code: string;
187
+ message: string;
188
+ }
189
+ interface PresenceUpdatedMessage {
190
+ type: 'presence_updated';
191
+ channel: string;
192
+ data: Record<string, unknown>;
193
+ }
194
+ interface PresenceLeftMessage {
195
+ type: 'presence_left';
196
+ channel: string;
197
+ }
198
+ type ServerMessage = ConnectedMessage | EventMessage | PresenceMessage | SubscribedMessage | UnsubscribedMessage | PublishedMessage | PongMessage | PingMessage | ServerErrorMessage | PresenceUpdatedMessage | PresenceLeftMessage;
199
+ interface ArkNotifyClientConfig {
200
+ baseUrl?: string;
201
+ token?: string | (() => string | null | undefined);
202
+ fetch?: typeof fetch;
203
+ }
204
+ interface AppCredentials {
205
+ appKey: string;
206
+ secret: string;
207
+ }
208
+ type PrivateChannelAuthHandler = (channel: string, connectionId: string) => Promise<string>;
209
+ interface ArkNotifyConnectionConfig {
210
+ baseUrl?: string;
211
+ appKey: string;
212
+ clientId?: string;
213
+ /** Signed connection token, or a resolver. Omit to auto-fetch when `clientId` is set. */
214
+ token?: string | (() => string | null | undefined);
215
+ /** App credentials for auto-fetching a connection token (backend-only when no serverAuthUrl). */
216
+ credentials?: AppCredentials;
217
+ /** Override server auth URL when auto-fetching a token; uses the application default when omitted. */
218
+ serverAuthUrl?: string | null;
219
+ /** Forwarded to the connection-token endpoint when auto-fetching a token. */
220
+ user_data?: Record<string, unknown>;
221
+ autoReconnect?: boolean;
222
+ reconnectDelayMs?: number;
223
+ maxReconnectDelayMs?: number;
224
+ onPrivateChannelAuth?: PrivateChannelAuthHandler;
225
+ fetch?: typeof fetch;
226
+ WebSocket?: typeof WebSocket;
227
+ }
228
+ interface SubscribeOptions {
229
+ history?: boolean;
230
+ presence?: boolean;
231
+ presence_data?: Record<string, unknown>;
232
+ auth?: string;
233
+ }
234
+ interface ArkNotifySSEConfig {
235
+ baseUrl?: string;
236
+ appKey: string;
237
+ channels: string[];
238
+ clientId?: string;
239
+ token?: string | (() => string | null | undefined);
240
+ auth?: Record<string, string>;
241
+ user_data?: Record<string, unknown>;
242
+ history?: boolean;
243
+ onPrivateChannelAuth?: PrivateChannelAuthHandler;
244
+ EventSource?: typeof EventSource;
245
+ }
246
+ interface ChannelEventHandler<T = unknown> {
247
+ (data: T, message: EventMessage): void;
248
+ }
249
+ interface ChannelHandlers {
250
+ [eventName: string]: ChannelEventHandler;
251
+ }
252
+ interface HealthResponse {
253
+ status: string;
254
+ uptime: number;
255
+ }
256
+
257
+ export type { AppCredentials as A, PublishedMessage as B, ConnectionTokenInput as C, ServerAuthApprovedResponse as D, EventMessage as E, ServerAuthDecision as F, ServerAuthDeniedResponse as G, HandleServerAuthOptions as H, ServerAuthRequest as I, ServerAuthResponse as J, ServerAuthTokenResponse as K, LoginInput as L, ServerErrorMessage as M, ServerMessage as N, SubscribeOptions as O, PingMessage as P, SubscribedMessage as Q, UpdateApplicationInput as R, ServerAuthAllowedResponse as S, User as T, UnsubscribedMessage as U, UserRole as V, ConnectionTokenResponse as a, ApiError as b, Application as c, ArkNotifyClientConfig as d, ArkNotifyConnectionConfig as e, ArkNotifySSEConfig as f, AuthResponse as g, ChannelAuthInput as h, ChannelAuthResponse as i, ChannelEventHandler as j, ChannelHandlers as k, ConnectedMessage as l, ConnectionCapabilities as m, ConnectionState as n, CreateApplicationInput as o, CreateAuthorizedServerAuthResponseOptions as p, HealthResponse as q, PongMessage as r, PresenceAction as s, PresenceLeftMessage as t, PresenceMemberInfo as u, PresenceMessage as v, PresenceUpdatedMessage as w, PrivateChannelAuthHandler as x, PublishEventInput as y, PublishEventResponse as z };