@ecoaloha/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,21 @@
1
+ # EcoAloha JavaScript SDK and CLI
2
+
3
+ Official source: https://github.com/ArneFfm/ecoaloha-agents.
4
+ API documentation: https://ecoaloha.com/developers.
5
+ Registry publication is pending. Node.js 20 or later is required.
6
+
7
+ From a checkout, run `npm install ./packages/sdk-js` in your integration project.
8
+
9
+ ```js
10
+ import { EcoAloha } from "@ecoaloha/sdk";
11
+ const client = new EcoAloha({ baseUrl: "https://ecoaloha.com/api/sandbox/v1" });
12
+ console.log(await client.experiences({ destinationId: "paris" }));
13
+ ```
14
+
15
+ Run `node packages/sdk-js/cli.js --sandbox destinations` from the repository root.
16
+ Run `ecoaloha --help` after package installation.
17
+
18
+ The client returns the complete response envelope, including `nextCursor`.
19
+ Use `request(path, options)` for other documented operations.
20
+ Errors expose `status`, `body`, and `retryAfter`. The client does not retry writes.
21
+ No key is required. Respect rate limits and affiliate disclosure rules.
package/cli.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import { EcoAloha } from "./index.js";
3
+
4
+ const args = process.argv.slice(2);
5
+ const sandbox = args[0] === "--sandbox";
6
+ if (sandbox) args.shift();
7
+ const [command, ...params] = args;
8
+ if (!command || command === "--help") {
9
+ console.log("Usage: ecoaloha [--sandbox] destinations | experiences destinationId [interest]");
10
+ } else {
11
+ try {
12
+ const client = new EcoAloha(sandbox ? { baseUrl: "https://ecoaloha.com/api/sandbox/v1" } : {});
13
+ let result;
14
+ if (command === "destinations" && params.length === 0) result = await client.destinations();
15
+ else if (command === "experiences" && params.length >= 1 && params.length <= 2) {
16
+ result = await client.experiences({ destinationId: params[0], interest: params[1] });
17
+ } else throw new Error("Unknown command or arguments. Run ecoaloha --help.");
18
+ console.log(JSON.stringify(result, null, 2));
19
+ } catch (error) {
20
+ console.error(error instanceof Error ? error.message : "Request failed");
21
+ process.exitCode = 1;
22
+ }
23
+ }
package/index.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ export interface ApiResponse<T = unknown> {
2
+ data: T;
3
+ nextCursor?: string | null;
4
+ }
5
+ export interface RequestOptions {
6
+ method?: "GET" | "POST" | "DELETE";
7
+ query?: Record<string, string | number | boolean | undefined>;
8
+ body?: unknown;
9
+ signal?: AbortSignal;
10
+ }
11
+ export class EcoAlohaError extends Error {
12
+ status: number;
13
+ body: unknown;
14
+ retryAfter: string | null;
15
+ }
16
+ export class EcoAloha {
17
+ constructor(options?: { baseUrl?: string; fetch?: typeof fetch });
18
+ request<T = unknown>(path: string, options?: RequestOptions): Promise<ApiResponse<T>>;
19
+ destinations(): Promise<ApiResponse<Array<{ id: string; name: string; country: string }>>>;
20
+ experiences(
21
+ query: { destinationId: string } & NonNullable<RequestOptions["query"]>,
22
+ ): Promise<ApiResponse<unknown[]>>;
23
+ compare(
24
+ experienceIds: string[],
25
+ currency?: "EUR" | "USD" | "GBP",
26
+ ): Promise<ApiResponse<unknown[]>>;
27
+ }
package/index.js ADDED
@@ -0,0 +1,48 @@
1
+ export class EcoAlohaError extends Error {
2
+ constructor(status, body, retryAfter) {
3
+ super(body?.detail ?? `EcoAloha request failed (${status})`);
4
+ this.status = status;
5
+ this.body = body;
6
+ this.retryAfter = retryAfter;
7
+ }
8
+ }
9
+
10
+ export class EcoAloha {
11
+ constructor({ baseUrl = "https://ecoaloha.com/api/v1", fetch: fetcher = globalThis.fetch } = {}) {
12
+ this.baseUrl = baseUrl.replace(/\/$/, "");
13
+ this.fetch = fetcher;
14
+ }
15
+
16
+ async request(path, { method = "GET", query = {}, body, signal } = {}) {
17
+ if (!/^\/[a-zA-Z0-9_/-]*$/.test(path) || path.includes("//")) {
18
+ throw new TypeError("Use an API-relative path, such as /destinations");
19
+ }
20
+ const url = new URL(this.baseUrl + path);
21
+ for (const [key, value] of Object.entries(query)) {
22
+ if (value !== undefined) url.searchParams.set(key, String(value));
23
+ }
24
+ const response = await this.fetch(url, {
25
+ method,
26
+ signal,
27
+ headers: {
28
+ Accept: "application/json",
29
+ ...(body === undefined ? {} : { "Content-Type": "application/json" }),
30
+ },
31
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
32
+ });
33
+ const payload = await response.json();
34
+ if (!response.ok)
35
+ throw new EcoAlohaError(response.status, payload, response.headers.get("Retry-After"));
36
+ return payload;
37
+ }
38
+
39
+ destinations() {
40
+ return this.request("/destinations");
41
+ }
42
+ experiences(query) {
43
+ return this.request("/experiences", { query });
44
+ }
45
+ compare(experienceIds, currency = "EUR") {
46
+ return this.request("/compare", { method: "POST", body: { experienceIds, currency } });
47
+ }
48
+ }
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@ecoaloha/sdk",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Official EcoAloha JavaScript SDK and CLI",
6
+ "homepage": "https://ecoaloha.com",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/ArneFfm/ecoaloha-agents.git",
10
+ "directory": "packages/sdk-js"
11
+ },
12
+ "license": "UNLICENSED",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./index.d.ts",
16
+ "import": "./index.js"
17
+ }
18
+ },
19
+ "bin": {
20
+ "ecoaloha": "cli.js"
21
+ },
22
+ "files": ["index.js", "index.d.ts", "cli.js", "README.md"],
23
+ "engines": {
24
+ "node": ">=20"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "scripts": {
30
+ "test": "node --test test/*.test.js"
31
+ }
32
+ }