@ecosplay/e-brocante 1.0.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.
- package/AGENTS.md +141 -0
- package/CHANGELOG.md +76 -0
- package/LICENSE +64 -0
- package/README.md +246 -0
- package/dist/EBrocanteClient.d.ts +38 -0
- package/dist/EBrocanteClient.d.ts.map +1 -0
- package/dist/EBrocanteClient.js +99 -0
- package/dist/EBrocanteClient.js.map +1 -0
- package/dist/cache/CacheBackend.d.ts +20 -0
- package/dist/cache/CacheBackend.d.ts.map +1 -0
- package/dist/cache/CacheBackend.js +6 -0
- package/dist/cache/CacheBackend.js.map +1 -0
- package/dist/cache/LocalCache.d.ts +22 -0
- package/dist/cache/LocalCache.d.ts.map +1 -0
- package/dist/cache/LocalCache.js +53 -0
- package/dist/cache/LocalCache.js.map +1 -0
- package/dist/cache/NullCache.d.ts +17 -0
- package/dist/cache/NullCache.d.ts.map +1 -0
- package/dist/cache/NullCache.js +26 -0
- package/dist/cache/NullCache.js.map +1 -0
- package/dist/cache/RedisCache.d.ts +39 -0
- package/dist/cache/RedisCache.d.ts.map +1 -0
- package/dist/cache/RedisCache.js +104 -0
- package/dist/cache/RedisCache.js.map +1 -0
- package/dist/cache/RedisConfig.d.ts +17 -0
- package/dist/cache/RedisConfig.d.ts.map +1 -0
- package/dist/cache/RedisConfig.js +13 -0
- package/dist/cache/RedisConfig.js.map +1 -0
- package/dist/cache/stableHashKey.d.ts +11 -0
- package/dist/cache/stableHashKey.d.ts.map +1 -0
- package/dist/cache/stableHashKey.js +27 -0
- package/dist/cache/stableHashKey.js.map +1 -0
- package/dist/exceptions/index.d.ts +41 -0
- package/dist/exceptions/index.d.ts.map +1 -0
- package/dist/exceptions/index.js +62 -0
- package/dist/exceptions/index.js.map +1 -0
- package/dist/http/HttpClient.d.ts +41 -0
- package/dist/http/HttpClient.d.ts.map +1 -0
- package/dist/http/HttpClient.js +222 -0
- package/dist/http/HttpClient.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/safeStrings.d.ts +52 -0
- package/dist/internal/safeStrings.d.ts.map +1 -0
- package/dist/internal/safeStrings.js +156 -0
- package/dist/internal/safeStrings.js.map +1 -0
- package/dist/logger/DebugWriter.d.ts +24 -0
- package/dist/logger/DebugWriter.d.ts.map +1 -0
- package/dist/logger/DebugWriter.js +62 -0
- package/dist/logger/DebugWriter.js.map +1 -0
- package/dist/logger/FileLogger.d.ts +26 -0
- package/dist/logger/FileLogger.d.ts.map +1 -0
- package/dist/logger/FileLogger.js +96 -0
- package/dist/logger/FileLogger.js.map +1 -0
- package/dist/logger/Logger.d.ts +12 -0
- package/dist/logger/Logger.d.ts.map +1 -0
- package/dist/logger/Logger.js +6 -0
- package/dist/logger/Logger.js.map +1 -0
- package/dist/logger/NullLogger.d.ts +13 -0
- package/dist/logger/NullLogger.d.ts.map +1 -0
- package/dist/logger/NullLogger.js +20 -0
- package/dist/logger/NullLogger.js.map +1 -0
- package/dist/models/Event.d.ts +37 -0
- package/dist/models/Event.d.ts.map +1 -0
- package/dist/models/Event.js +42 -0
- package/dist/models/Event.js.map +1 -0
- package/dist/models/Orga.d.ts +33 -0
- package/dist/models/Orga.d.ts.map +1 -0
- package/dist/models/Orga.js +39 -0
- package/dist/models/Orga.js.map +1 -0
- package/dist/models/PaginatedResult.d.ts +27 -0
- package/dist/models/PaginatedResult.d.ts.map +1 -0
- package/dist/models/PaginatedResult.js +46 -0
- package/dist/models/PaginatedResult.js.map +1 -0
- package/dist/resources/EventsResource.d.ts +20 -0
- package/dist/resources/EventsResource.d.ts.map +1 -0
- package/dist/resources/EventsResource.js +46 -0
- package/dist/resources/EventsResource.js.map +1 -0
- package/dist/resources/OrgaResource.d.ts +20 -0
- package/dist/resources/OrgaResource.d.ts.map +1 -0
- package/dist/resources/OrgaResource.js +51 -0
- package/dist/resources/OrgaResource.js.map +1 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
- package/src/EBrocanteClient.ts +110 -0
- package/src/cache/CacheBackend.ts +20 -0
- package/src/cache/LocalCache.ts +64 -0
- package/src/cache/NullCache.ts +32 -0
- package/src/cache/RedisCache.ts +123 -0
- package/src/cache/RedisConfig.ts +23 -0
- package/src/cache/stableHashKey.ts +28 -0
- package/src/exceptions/index.ts +75 -0
- package/src/http/HttpClient.ts +266 -0
- package/src/index.ts +42 -0
- package/src/internal/safeStrings.ts +154 -0
- package/src/logger/DebugWriter.ts +61 -0
- package/src/logger/FileLogger.ts +106 -0
- package/src/logger/Logger.ts +13 -0
- package/src/logger/NullLogger.ts +22 -0
- package/src/models/Event.ts +92 -0
- package/src/models/Orga.ts +76 -0
- package/src/models/PaginatedResult.ts +62 -0
- package/src/resources/EventsResource.ts +48 -0
- package/src/resources/OrgaResource.ts +53 -0
- package/src/types.ts +86 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# AGENTS.md — Policy for AI assistants
|
|
2
|
+
|
|
3
|
+
> This file describes what AI assistants (Claude, ChatGPT, GitHub Copilot, Cursor, Continue, etc.) **can and cannot do** with this source code. It builds on the emerging [agents.md](https://agents.md/) convention and supplements [`LICENSE`](LICENSE).
|
|
4
|
+
>
|
|
5
|
+
> Alias: this file is also reachable as `ai.md` (symlink) for tools that follow that other naming.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🎯 Purpose
|
|
10
|
+
|
|
11
|
+
This SDK (`@ecosplay/e-brocante`, distributed publicly on npm and Gitea `code.e-cosplay.fr/shoko/e-brocante-js`) is protected by a **proprietary license**. It is provided free of charge by **Association E-Cosplay** (`contact@e-cosplay.fr`) so that developers and their AI assistants can **integrate the public e-brocante.enum.fr API**.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## ✅ What AI assistants ARE allowed to do
|
|
16
|
+
|
|
17
|
+
To help the user **integrate the e-brocante API**:
|
|
18
|
+
|
|
19
|
+
1. **Read** the entire source (`src/`, `tests/`, `samples/`, `docs/`) to understand how to use it.
|
|
20
|
+
2. **Read** the documentation (`README.md`, `docs/*.md`, `CHANGELOG.md`) to explain the SDK to the user.
|
|
21
|
+
3. **Write code in the user's project** that consumes this SDK (e.g. generate a Next.js / Nuxt / Express service that calls `client.events.list(...)`).
|
|
22
|
+
4. **Suggest** appropriate SDK calls based on the user's needs.
|
|
23
|
+
5. **Explain** parameters, TypeScript types, error codes, prod/test mode, cache and logger options.
|
|
24
|
+
6. **Help debug** an integration: analyze logs, HTTP errors, propose fixes in the user's code (not in the SDK).
|
|
25
|
+
7. **Quote short snippets** (≤ ~10 lines) inside an explanation, attributing the source as `// from @ecosplay/e-brocante (proprietary license)`.
|
|
26
|
+
8. **Reference** this SDK from other projects (e.g. add `bun add @ecosplay/e-brocante` to a script).
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## ❌ What AI assistants ARE NOT allowed to do
|
|
31
|
+
|
|
32
|
+
Without prior written authorization from Association E-Cosplay (`contact@e-cosplay.fr`):
|
|
33
|
+
|
|
34
|
+
1. **Modify** the SDK source code (files in this repository). No automatic PR / commit / patch without human review and the publisher's agreement — see clauses 3.b and 4 of the [LICENSE](LICENSE).
|
|
35
|
+
2. **Republish** any portion of this code to another registry, public repo, gist, pastebin, distributed fork, etc.
|
|
36
|
+
3. **Create derivative works**: a new SDK, a port to another language, a rebranded fork, integration into a competing product.
|
|
37
|
+
4. **Copy** large sections (> ~20 lines or substantive logic) into the user's code.
|
|
38
|
+
5. **Remove or hide** license headers (`@license Proprietary`), copyright notices, or references to Association E-Cosplay.
|
|
39
|
+
6. **Train or fine-tune** an AI model on this source code, for commercial purposes or public redistribution.
|
|
40
|
+
7. **Use the names** "e-brocante", "E-Cosplay", or "ENUM" to promote a derivative or competing product.
|
|
41
|
+
|
|
42
|
+
> Any pull request, issue, or suggestion submitted to this repository by an AI agent automatically implies the irrevocable, free, non-exclusive transfer of patrimonial rights to Association E-Cosplay (clause 4 of the LICENSE).
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 🛠️ Integration recommendations
|
|
47
|
+
|
|
48
|
+
### 1. Check the installed version
|
|
49
|
+
|
|
50
|
+
Read `package.json` and the lockfile (`bun.lockb`, `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`). Adapt your suggestions to that version (consult [`CHANGELOG.md`](CHANGELOG.md) when needed).
|
|
51
|
+
|
|
52
|
+
### 2. Suggest sandbox mode for development
|
|
53
|
+
|
|
54
|
+
Always recommend starting in `mode: 'test'` (sandbox `/api/test/*`) during dev and CI:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
import { EBrocanteClient } from '@ecosplay/e-brocante';
|
|
58
|
+
|
|
59
|
+
const client = new EBrocanteClient({
|
|
60
|
+
apiKey: 'eb_test_…', // from https://e-brocante.enum.fr/espace-orga/api
|
|
61
|
+
brocEmail: 'dev@example.com',
|
|
62
|
+
mode: 'test', // ← sandbox: 1 fake orga + 3-7 fake events
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 3. API key safety — ⚠️ very important on the JS side
|
|
67
|
+
|
|
68
|
+
- **Always** read the key from a server-side env variable (`process.env.EBROCANTE_API_KEY`, `Bun.env.…`).
|
|
69
|
+
- **NEVER** bundle the key in a browser build (Vite, Webpack, Rollup) — it would be visible to every visitor. The SDK runs **server-side** (Node/Bun), not in the browser, for authenticated calls.
|
|
70
|
+
- For frontend apps, proxy through your backend.
|
|
71
|
+
- **Never** commit the key, **never** log it, **never** push it.
|
|
72
|
+
- Recommend rotating the key (from `/espace-broc/api` or `/espace-orga/api`) if it might have leaked.
|
|
73
|
+
|
|
74
|
+
### 4. Cache & rate limiting
|
|
75
|
+
|
|
76
|
+
- Default cache: 5 min in-memory. For multi-instance Node deployments (Kubernetes, PM2 cluster), suggest `cacheType: 'redis'`.
|
|
77
|
+
- Default rate limit: **60 req/min** and **10,000 req/day** per key. Suggest reasonable polling (15-60 min for events).
|
|
78
|
+
- The SDK auto-retries on 429 / 5xx (max 3 times) honoring `Retry-After`. No custom logic needed.
|
|
79
|
+
|
|
80
|
+
### 5. Pagination
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
for await (const event of client.events.iter({ city: 'paris' })) {
|
|
84
|
+
// automatic pagination across pages (AsyncIterable)
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 6. Typed errors
|
|
89
|
+
|
|
90
|
+
All errors extend `EBrocanteError`. Useful subclasses:
|
|
91
|
+
|
|
92
|
+
- `AuthenticationError` (401, 403)
|
|
93
|
+
- `RateLimitError` (429) — exposes `retryAfter: number`
|
|
94
|
+
- `NotFoundError` (404, 410)
|
|
95
|
+
- `ValidationError` (422) — exposes the raw `body`
|
|
96
|
+
- `ServerError` (5xx)
|
|
97
|
+
- `NetworkError` (timeout, DNS, etc.)
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { RateLimitError, NotFoundError } from '@ecosplay/e-brocante';
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const event = await client.events.get('definitely-not-real');
|
|
104
|
+
} catch (e) {
|
|
105
|
+
if (e instanceof NotFoundError) { /* … */ }
|
|
106
|
+
else if (e instanceof RateLimitError) { /* wait e.retryAfter seconds */ }
|
|
107
|
+
else throw e;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 7. Strict TypeScript
|
|
112
|
+
|
|
113
|
+
The SDK ships with `strict: true`, `noUncheckedIndexedAccess: true`, `exactOptionalPropertyTypes: true`. Suggest enabling these flags in the user's project for maximum type safety.
|
|
114
|
+
|
|
115
|
+
### 8. Bundled mock API
|
|
116
|
+
|
|
117
|
+
For tests, point the client at the bundled mock server in `tools/mock-api/` (started with `bun run mock-api`) using `baseUrl: 'http://127.0.0.1:3000'` and `apiKey: 'eb_mock_dev'`. No real API key needed.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 📚 Useful links for agents
|
|
122
|
+
|
|
123
|
+
| Resource | URL |
|
|
124
|
+
|----------|-----|
|
|
125
|
+
| **Official API docs** | https://e-brocante.enum.fr/api/doc |
|
|
126
|
+
| **OpenAPI 3.1 spec (JSON)** | https://e-brocante.enum.fr/api/doc.json |
|
|
127
|
+
| **Platform** | https://e-brocante.enum.fr |
|
|
128
|
+
| **Get an API key (stallholder)** | https://e-brocante.enum.fr/espace-broc/api |
|
|
129
|
+
| **Get an API key (organizer)** | https://e-brocante.enum.fr/espace-orga/api |
|
|
130
|
+
| **SDK changelog** | [CHANGELOG.md](CHANGELOG.md) |
|
|
131
|
+
| **License** | [LICENSE](LICENSE) |
|
|
132
|
+
| **Issues** | https://code.e-cosplay.fr/shoko/e-brocante-js/issues |
|
|
133
|
+
| **Publisher contact** | contact@e-cosplay.fr |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## ⚖️ Legal recap in one line
|
|
138
|
+
|
|
139
|
+
> You may **read this code and help use it**; you may **not modify, republish, or derive from it** without written permission from Association E-Cosplay (`contact@e-cosplay.fr`).
|
|
140
|
+
|
|
141
|
+
Any violation triggers immediate termination of the right of use (clause 6 of the LICENSE).
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
Format based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
strict adherence to [SemVer 2.0.0](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.0.2] - 2026-05-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- 5 new advanced samples in `samples/` (alongside the four endpoint examples shipped in 1.0.0):
|
|
14
|
+
- `05-error-handling.ts` — typed exception matching (`NotFoundError`, `RateLimitError`, `AuthenticationError`, `ValidationError`, `NetworkError`, `ServerError`).
|
|
15
|
+
- `06-cache-strategies.ts` — local TTL, Redis (commented), `cache: false`, `skipCache: true`, manual `purge()` / `purgeKey()`.
|
|
16
|
+
- `07-debug-and-logging.ts` — `debug:true` → DEBUG.TXT, `logPath` → JSON-lines per day, custom `Logger`.
|
|
17
|
+
- `08-pagination-iter.ts` — streaming via the `iter()` AsyncIterable.
|
|
18
|
+
- `09-sandbox-mode.ts` — sandbox contract assertions (exactly 1 fake orga + 3–7 fake events).
|
|
19
|
+
- README: SonarQube quality badges now embed the project badge token (replacing the previous `__SET_BADGE_TOKEN__` placeholder).
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- `RedisCache.connect()`: invert the factory check so the live-Redis fallback (with its `if (!this.clientFactory)` guard) sits entirely inside `/* v8 ignore start/stop */`. Eliminates the partial-coverage flag SonarQube was reporting on the `??` ternary.
|
|
23
|
+
- `HttpClient.secureJitter()`: replace the `??` with an explicit `if (raw === undefined)` guard annotated `/* v8 ignore next */` (the branch is unreachable since `crypto.getRandomValues` always populates the array).
|
|
24
|
+
- `HttpClient.attemptOrRetryConnect`: drop the `try/catch/finally` pattern in favour of `try/catch` + explicit `clearTimeout(timer)` in both paths; error handling delegated to a new `handleConnectError()` helper. Removes the phantom finally branch SonarQube was flagging.
|
|
25
|
+
- `FileLogger.write`: move the `/* v8 ignore */` block boundary so the catch is fully suppressed without hiding the success path.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- `RedisCache.connect()` extracted live-Redis logic into a dedicated `realConnect()` method covered by `/* v8 ignore start/stop */`. The factory-driven hot path is branchless.
|
|
29
|
+
- Centralize the `asRecord` and `asNullableInt` helpers in `src/internal/safeStrings.ts` (no more local copies in `Event.ts` / `Orga.ts`); fully unit-tested.
|
|
30
|
+
|
|
31
|
+
## [1.0.1] - 2026-05-10
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
- Centralize string/coercion helpers in `src/internal/safeStrings.ts` (`asString`, `asNullableString`, `asNumber`, `asBoolean`, `asDate`, `stringifyPrimitive`, `isValidEmail`, `trimLeadingSlash`, `trimTrailingSlash`, `trimSlashes`, `slashesToDots`). Removes duplicated `nullableString` / `parseDate` between `Event.ts` and `Orga.ts` (SonarQube duplicated-block report).
|
|
35
|
+
- `Event.ts` / `Orga.ts` rewritten on top of the safeStrings helpers — much shorter, no more `String(unknown)` calls that risked the `[object Object]` foot-gun.
|
|
36
|
+
- Drop literal types overridden by `string` in union aliases: `EventCategory`, `EventState`, `PricingMode`, `LegalForm`, `OrgaState` are now plain `string` (SonarQube S6571).
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
- ReDoS hardening (SonarQube S5852): replace every regex used on user-controlled input with a hand-rolled O(n) helper. `EMAIL_RE`, `baseUrl.replace(/\/+$/)`, `endpoint.replace(/^\/+/)`, `endpoint.replace(/^\/+|\/+$/g, '')`, `String.replace(/\//g, '.')` are all gone.
|
|
40
|
+
- `String#sort()` now passes a `localeCompare(b, 'en')` comparator so cache-key ordering is fully deterministic (SonarQube S2871).
|
|
41
|
+
- `HttpClient.encodeQuery` extracts the boolean/string ternary into the shared `stringifyPrimitive` helper that throws on non-primitive values (SonarQube S3358 + S6551 + S6571).
|
|
42
|
+
- `NullLogger` and `NullCache` no-op methods now carry a `/* intentional no-op */` body so Biome / SonarQube `S1186` (empty method) is satisfied.
|
|
43
|
+
- `CacheBackend.get()` return type collapsed from `Promise<unknown | null>` to `Promise<unknown>` (`unknown | null` is structurally `unknown`).
|
|
44
|
+
- `Event.ts` and `Orga.ts` no longer use `as unknown as Record<…>` casts (SonarQube S4325).
|
|
45
|
+
- `parseDate` flow rewritten without a leading negation (SonarQube S7735 — unexpected negated condition).
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
- 31 new unit assertions in `tests/unit/safeStrings.test.ts` exercising every helper (incl. error paths of `stringifyPrimitive` and `isValidEmail`).
|
|
49
|
+
|
|
50
|
+
## [1.0.0] - 2026-05-10
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
- Initial release of the e-brocante JS / TypeScript SDK.
|
|
54
|
+
- `EBrocanteClient` constructor with options for API key, account email, mode (`prod`/`test`), base URL override, HTTP timeout, retries, and a custom `fetch` implementation.
|
|
55
|
+
- Read-only resources: `events.list()`, `events.get(slug)`, `events.iter()`, `orga.list()`, `orga.get(slug)`, `orga.iter()` (all four public endpoints).
|
|
56
|
+
- Strict TypeScript types: `EventDto`, `OrgaDto`, `PaginatedResult<T>`, `EventFilters`, `OrgaFilters`, `EBrocanteClientOptions`, …
|
|
57
|
+
- Response cache with three modes:
|
|
58
|
+
- `local` (in-memory, default, TTL 5 min)
|
|
59
|
+
- `redis` (via `ioredis`, multi-instance friendly, optional dependency)
|
|
60
|
+
- `cache: false` (NullCache)
|
|
61
|
+
- Per-call `skipCache` option and `client.cache.purge()` / `purgeKey()` helpers.
|
|
62
|
+
- `Logger` interface with `NullLogger` (default), `FileLogger` (JSON-lines per day, sensitive header redaction), and the ability to inject any custom logger.
|
|
63
|
+
- Verbose `debug: true` mode that appends one line per action to a `DEBUG.TXT` file (configurable path).
|
|
64
|
+
- Typed exception hierarchy: `EBrocanteError`, `AuthenticationError`, `NotFoundError`, `RateLimitError` (carries `retryAfter`), `ValidationError` (carries the raw `body`), `ServerError`, `NetworkError`.
|
|
65
|
+
- Automatic retry on 429 / 5xx with exponential backoff and CSPRNG-driven jitter (max 3 retries by default, honors `Retry-After`).
|
|
66
|
+
- Bundled mock API server in `tools/mock-api/` (Bun + TypeScript) reproducing the four endpoints with realistic fixtures.
|
|
67
|
+
- Four runnable samples in `samples/` (`bun run samples/0X-…ts`).
|
|
68
|
+
- bun/npm scripts: `test`, `test:unit`, `test:integration`, `test:coverage`, `coverage:check`, `lint`, `typecheck`, `mock-api`, `ci`, `build`.
|
|
69
|
+
|
|
70
|
+
### Security
|
|
71
|
+
- API key + `Authorization` header values automatically redacted from `FileLogger` output and the request-context fed to the logger.
|
|
72
|
+
|
|
73
|
+
[Unreleased]: https://code.e-cosplay.fr/shoko/e-brocante-js/compare/v1.0.2...HEAD
|
|
74
|
+
[1.0.2]: https://code.e-cosplay.fr/shoko/e-brocante-js/compare/v1.0.1...v1.0.2
|
|
75
|
+
[1.0.1]: https://code.e-cosplay.fr/shoko/e-brocante-js/compare/v1.0.0...v1.0.1
|
|
76
|
+
[1.0.0]: https://code.e-cosplay.fr/shoko/e-brocante-js/releases/tag/v1.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Proprietary license — e-brocante SDK
|
|
2
|
+
Copyright (c) 2026 Association E-Cosplay (RNA W022006988, SIREN 943121517)
|
|
3
|
+
Contact: contact@e-cosplay.fr
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
1. PURPOSE
|
|
7
|
+
This software (the "SDK") is made available by Association E-Cosplay (the
|
|
8
|
+
"Publisher") to allow third-party developers to interact with the public
|
|
9
|
+
API of e-brocante.enum.fr.
|
|
10
|
+
|
|
11
|
+
2. RIGHTS GRANTED
|
|
12
|
+
The Publisher grants any individual or legal entity (the "User") a personal,
|
|
13
|
+
non-exclusive, non-transferable, revocable right to:
|
|
14
|
+
a) download and install the SDK;
|
|
15
|
+
b) use the SDK to interact with the e-brocante.enum.fr API, for
|
|
16
|
+
commercial or non-commercial purposes, subject to compliance with
|
|
17
|
+
the public API terms of service.
|
|
18
|
+
|
|
19
|
+
3. RESTRICTIONS — EXPRESS PROHIBITIONS
|
|
20
|
+
Unless WRITTEN and PRIOR authorization is obtained from the Publisher
|
|
21
|
+
(contact@e-cosplay.fr), the following are strictly forbidden:
|
|
22
|
+
a) REDISTRIBUTION of the SDK in any form, including:
|
|
23
|
+
- republication on another registry (npm, Packagist, GitHub,
|
|
24
|
+
another Git forge, etc.),
|
|
25
|
+
- inclusion in a third-party software distribution,
|
|
26
|
+
- hosting a public or private mirror;
|
|
27
|
+
b) MODIFICATION of the source code, including:
|
|
28
|
+
- publicly distributed forks,
|
|
29
|
+
- production of derivative works,
|
|
30
|
+
- integration of unvalidated patches;
|
|
31
|
+
c) REMOVAL or concealment of copyright notices, license headers, or
|
|
32
|
+
references to the Publisher;
|
|
33
|
+
d) USE of the names "e-brocante", "E-Cosplay", or associated marks
|
|
34
|
+
to promote a derivative or competing product;
|
|
35
|
+
e) REVERSE ENGINEERING of the protected components beyond what is
|
|
36
|
+
strictly necessary for interoperability (art. L122-6-1 of the
|
|
37
|
+
French Intellectual Property Code).
|
|
38
|
+
|
|
39
|
+
4. EXTERNAL CONTRIBUTIONS
|
|
40
|
+
Any pull request, issue, or suggestion submitted to the public repository
|
|
41
|
+
implies the irrevocable, free, and non-exclusive transfer to the Publisher
|
|
42
|
+
of the patrimonial rights over the contribution, so that it may or may not
|
|
43
|
+
be incorporated into the official SDK. No obligation to incorporate.
|
|
44
|
+
No remuneration.
|
|
45
|
+
|
|
46
|
+
5. NO WARRANTY
|
|
47
|
+
The SDK is provided "AS IS", without warranty of any kind, express or
|
|
48
|
+
implied, including but not limited to warranties of merchantability,
|
|
49
|
+
fitness for a particular purpose, or non-infringement. The Publisher
|
|
50
|
+
shall not be liable for any direct or indirect damages resulting from
|
|
51
|
+
the use of the SDK.
|
|
52
|
+
|
|
53
|
+
6. TERMINATION
|
|
54
|
+
Any breach of clauses 3 or 4 results in the immediate and automatic
|
|
55
|
+
termination of the right of use defined in clause 2, without prejudice
|
|
56
|
+
to any damages.
|
|
57
|
+
|
|
58
|
+
7. GOVERNING LAW
|
|
59
|
+
This license is governed by French law. Any dispute falls under the
|
|
60
|
+
exclusive jurisdiction of the Tribunal Judiciaire of Saint-Quentin
|
|
61
|
+
(02, France).
|
|
62
|
+
|
|
63
|
+
For any express authorization request (redistribution, fork,
|
|
64
|
+
modification, partnership): contact@e-cosplay.fr
|
package/README.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# e-brocante JavaScript / TypeScript SDK
|
|
2
|
+
|
|
3
|
+
> Official JS / TypeScript client for the **public API of [e-brocante.enum.fr](https://e-brocante.enum.fr)** — the French platform connecting flea-market organizers and stallholders (brocanteurs).
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://nodejs.org/)
|
|
7
|
+
[](https://bun.sh/)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://e-brocante.enum.fr/api/doc)
|
|
10
|
+
|
|
11
|
+
### Code quality
|
|
12
|
+
|
|
13
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-brocante-js)
|
|
14
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-brocante-js)
|
|
15
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-brocante-js)
|
|
16
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-brocante-js)
|
|
17
|
+
[](https://sn.e-cosplay.fr/dashboard?id=e-brocante-js)
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## ✨ Overview
|
|
22
|
+
|
|
23
|
+
Idiomatic TypeScript client for the **four public read-only endpoints** of the e-brocante API:
|
|
24
|
+
|
|
25
|
+
- 🎪 **Events** — list & detail (flea markets, vide-greniers, antique markets, collector fairs)
|
|
26
|
+
- 👥 **Organizers** — list & profile
|
|
27
|
+
|
|
28
|
+
The API is **free**, **REST + JSON**, and **strictly read-only** (`GET` only). All transactional operations (booth reservations, payments, attendance register) live in the e-brocante.enum.fr web app, not in this SDK.
|
|
29
|
+
|
|
30
|
+
> 🔗 **Site**: https://e-brocante.enum.fr
|
|
31
|
+
> 📖 **API docs**: https://e-brocante.enum.fr/api/doc
|
|
32
|
+
> 🐛 **Issues**: https://code.e-cosplay.fr/shoko/e-brocante-js/issues
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 📦 Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# npm
|
|
40
|
+
npm install @ecosplay/e-brocante
|
|
41
|
+
|
|
42
|
+
# pnpm
|
|
43
|
+
pnpm add @ecosplay/e-brocante
|
|
44
|
+
|
|
45
|
+
# bun
|
|
46
|
+
bun add @ecosplay/e-brocante
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or pull from the **Gitea Composer/npm registry** (private/internal channel):
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
bun add git+https://code.e-cosplay.fr/shoko/e-brocante-js.git#v1.0.0
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Requirements**: Node.js **≥ 22** or Bun **≥ 1.1**, TypeScript ≥ 5.4 recommended. The SDK uses native `fetch` and `crypto` — no `axios`/`got`/`node-fetch` dependency.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 🔑 Getting an API key
|
|
60
|
+
|
|
61
|
+
The API authenticates with **two headers**:
|
|
62
|
+
|
|
63
|
+
| Header | Meaning |
|
|
64
|
+
|--------|---------|
|
|
65
|
+
| **`X-KEY`** | Your API key (`eb_prod_…` for live, `eb_test_…` for sandbox) |
|
|
66
|
+
| **`X-BROC`** | The email of the e-brocante account the call is made for |
|
|
67
|
+
|
|
68
|
+
### Step 1 — Create an account
|
|
69
|
+
|
|
70
|
+
- Stallholder → https://e-brocante.enum.fr/register/broc
|
|
71
|
+
- Organizer → https://e-brocante.enum.fr/register/orga
|
|
72
|
+
|
|
73
|
+
### Step 2 — Generate an API key
|
|
74
|
+
|
|
75
|
+
| Account type | API key page |
|
|
76
|
+
|--------------|--------------|
|
|
77
|
+
| Stallholder (`ROLE_BROC`) | **https://e-brocante.enum.fr/espace-broc/api** |
|
|
78
|
+
| Organizer (`ROLE_ORGA`) | **https://e-brocante.enum.fr/espace-orga/api** |
|
|
79
|
+
|
|
80
|
+
Pick **prod** (`eb_prod_…`) for production data, **test** (`eb_test_…`) for the sandbox. Keys are shown once.
|
|
81
|
+
|
|
82
|
+
> ⚠️ Read the key from an environment variable. **Never bundle it in a browser build** — the SDK is server-side. For frontend apps, proxy through your own backend.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 🚀 Quick start
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { EBrocanteClient } from '@ecosplay/e-brocante';
|
|
90
|
+
|
|
91
|
+
const client = new EBrocanteClient({
|
|
92
|
+
apiKey: process.env.EBROCANTE_API_KEY!, // eb_prod_...
|
|
93
|
+
brocEmail: 'orga@example.com',
|
|
94
|
+
mode: 'prod',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const { data: events } = await client.events.list({
|
|
98
|
+
city: 'paris',
|
|
99
|
+
from: '2026-06-01',
|
|
100
|
+
to: '2026-06-30',
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
for (const event of events) {
|
|
104
|
+
console.log(`${event.name} — ${event.city} on ${event.startAt.toISOString().slice(0, 10)}`);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
More runnable examples in [`samples/`](samples/).
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🟢 Live vs 🟡 Sandbox
|
|
113
|
+
|
|
114
|
+
| Mode | URL prefix | Key prefix | Data | Stripe |
|
|
115
|
+
|------|------------|------------|------|--------|
|
|
116
|
+
| 🟢 **`prod`** (live) | `/api/prod/*` | `eb_prod_…` | **Real** organizers and events | Stripe **live** |
|
|
117
|
+
| 🟡 **`test`** (sandbox) | `/api/test/*` | `eb_test_…` | **Fake** (1 fake orga + 3-7 fake events, deterministic per day) | Stripe **test** |
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const client = new EBrocanteClient({ apiKey: 'eb_test_xxx', brocEmail: 'tester@example.com', mode: 'test' });
|
|
121
|
+
const { data: orgas } = await client.orga.list();
|
|
122
|
+
console.assert(orgas.length === 1 && orgas[0]?.slug === 'test-orga');
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
> 💡 Develop and test in `mode: 'test'`, then flip to `mode: 'prod'`. **Data never crosses between modes.**
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## ⚡ Response cache
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { EBrocanteClient } from '@ecosplay/e-brocante';
|
|
133
|
+
|
|
134
|
+
// 1) Local in-memory cache (default, TTL 5 min)
|
|
135
|
+
const client = new EBrocanteClient({ apiKey: '...', brocEmail: '...' });
|
|
136
|
+
|
|
137
|
+
// 2) Local with custom TTL
|
|
138
|
+
const c2 = new EBrocanteClient({ apiKey: '...', brocEmail: '...', cacheTtl: 3600 });
|
|
139
|
+
|
|
140
|
+
// 3) Redis (multi-instance — requires `bun add ioredis`)
|
|
141
|
+
const c3 = new EBrocanteClient({
|
|
142
|
+
apiKey: '...',
|
|
143
|
+
brocEmail: '...',
|
|
144
|
+
cacheType: 'redis',
|
|
145
|
+
cacheRedis: { host: 'redis.internal', port: 6379, db: 3 },
|
|
146
|
+
cacheTtl: 600,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// 4) Disabled
|
|
150
|
+
const c4 = new EBrocanteClient({ apiKey: '...', brocEmail: '...', cache: false });
|
|
151
|
+
|
|
152
|
+
// 5) One-shot bypass
|
|
153
|
+
const fresh = await client.events.list({ city: 'paris' }, { skipCache: true });
|
|
154
|
+
|
|
155
|
+
// 6) Invalidation
|
|
156
|
+
await client.cache.purge();
|
|
157
|
+
await client.cache.purgeKey('events.list', { city: 'paris' });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 🪵 Logging
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { EBrocanteClient } from '@ecosplay/e-brocante';
|
|
166
|
+
|
|
167
|
+
// A) Built-in JSON-lines file logger (one file per day)
|
|
168
|
+
new EBrocanteClient({ apiKey: '...', brocEmail: '...', logPath: '/var/log/ebrocante' });
|
|
169
|
+
|
|
170
|
+
// B) Plug your own Logger (any `{ debug, info, warn, error }` interface)
|
|
171
|
+
new EBrocanteClient({ apiKey: '...', brocEmail: '...', logger: pino() });
|
|
172
|
+
|
|
173
|
+
// C) Verbose plain-text trace to ./DEBUG.TXT (one line per action)
|
|
174
|
+
new EBrocanteClient({ apiKey: '...', brocEmail: '...', debug: true });
|
|
175
|
+
new EBrocanteClient({ apiKey: '...', brocEmail: '...', debug: true, debugFile: '/tmp/ebr.debug' });
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
API keys and `Authorization` headers are automatically redacted from log output.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## 📡 Available endpoints
|
|
183
|
+
|
|
184
|
+
| SDK method | API endpoint | Description |
|
|
185
|
+
|------------|--------------|-------------|
|
|
186
|
+
| `client.events.list(filters)` | `GET /api/{mode}/events` | Paginated list (geo / date / category filters) |
|
|
187
|
+
| `client.events.get(slug)` | `GET /api/{mode}/event/{slug}` | Event detail |
|
|
188
|
+
| `client.orga.list(filters)` | `GET /api/{mode}/orga` | Paginated list of organizers |
|
|
189
|
+
| `client.orga.get(slug)` | `GET /api/{mode}/orga/{slug}` | Organizer profile + upcoming events |
|
|
190
|
+
|
|
191
|
+
Both `events` and `orga` resources expose an `iter()` `AsyncIterable` that walks every page automatically.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## 🧪 Bundled mock API server
|
|
196
|
+
|
|
197
|
+
The package ships a Bun-based mock server in [`tools/mock-api/`](tools/mock-api/) reproducing the four endpoints on `http://127.0.0.1:3000`. Use it for local development, integration tests, and CI without ever touching production.
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
bun run mock-api
|
|
201
|
+
# → server listening on http://127.0.0.1:3000
|
|
202
|
+
|
|
203
|
+
# In another terminal:
|
|
204
|
+
EBROCANTE_BASE_URL=http://127.0.0.1:3000 bun run samples/01-list-events.ts
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Point any client at it via `baseUrl`:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
const client = new EBrocanteClient({
|
|
211
|
+
apiKey: 'eb_mock_dev',
|
|
212
|
+
brocEmail: 'dev@example.com',
|
|
213
|
+
mode: 'prod',
|
|
214
|
+
baseUrl: 'http://127.0.0.1:3000',
|
|
215
|
+
});
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
See [`tools/mock-api/README.md`](tools/mock-api/README.md) for fixtures and configuration.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 🤖 AI assistants
|
|
223
|
+
|
|
224
|
+
This SDK is compatible with AI coding assistants (Claude, ChatGPT, Copilot, Cursor, …) **for read-only integration help**. Modifying or republishing the SDK requires written authorization from the publisher. See [`AGENTS.md`](AGENTS.md).
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## 📜 License
|
|
229
|
+
|
|
230
|
+
**Proprietary** — © 2026 Association E-Cosplay (RNA W022006988, SIREN 943121517).
|
|
231
|
+
|
|
232
|
+
The SDK is **free to use**. **Redistribution, modification, distributed forks, and derivative works are forbidden without prior written agreement** — `contact@e-cosplay.fr`.
|
|
233
|
+
|
|
234
|
+
See [`LICENSE`](LICENSE) for the full text.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 🔗 Useful links
|
|
239
|
+
|
|
240
|
+
- 🌸 **Platform**: https://e-brocante.enum.fr
|
|
241
|
+
- 📖 **API docs**: https://e-brocante.enum.fr/api/doc
|
|
242
|
+
- 🔐 **API keys (stallholder)**: https://e-brocante.enum.fr/espace-broc/api
|
|
243
|
+
- 🔐 **API keys (organizer)**: https://e-brocante.enum.fr/espace-orga/api
|
|
244
|
+
- 🐛 **Issues**: https://code.e-cosplay.fr/shoko/e-brocante-js/issues
|
|
245
|
+
- 💬 **Contact**: contact@e-cosplay.fr
|
|
246
|
+
- 🏢 **Publisher**: Association E-Cosplay — https://e-cosplay.fr
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* e-brocante JavaScript / TypeScript SDK
|
|
3
|
+
*
|
|
4
|
+
* @copyright 2026 Association E-Cosplay
|
|
5
|
+
* @author Association E-Cosplay <contact@e-cosplay.fr>
|
|
6
|
+
* @license Proprietary — see LICENSE
|
|
7
|
+
*
|
|
8
|
+
* Redistribution and modification forbidden without prior written agreement.
|
|
9
|
+
* Contact: contact@e-cosplay.fr
|
|
10
|
+
*/
|
|
11
|
+
import type { CacheBackend } from './cache/CacheBackend.js';
|
|
12
|
+
import type { Logger } from './logger/Logger.js';
|
|
13
|
+
import { EventsResource } from './resources/EventsResource.js';
|
|
14
|
+
import { OrgaResource } from './resources/OrgaResource.js';
|
|
15
|
+
import type { EBrocanteClientOptions } from './types.js';
|
|
16
|
+
/**
|
|
17
|
+
* Main client for the e-brocante.enum.fr public API.
|
|
18
|
+
*
|
|
19
|
+
* const client = new EBrocanteClient({
|
|
20
|
+
* apiKey: 'eb_prod_…',
|
|
21
|
+
* brocEmail: 'orga@example.com',
|
|
22
|
+
* });
|
|
23
|
+
* const { data: events } = await client.events.list({ city: 'paris' });
|
|
24
|
+
* const orga = await client.orga.get('asso-sakura');
|
|
25
|
+
*/
|
|
26
|
+
export declare class EBrocanteClient {
|
|
27
|
+
static readonly VERSION: string;
|
|
28
|
+
readonly events: EventsResource;
|
|
29
|
+
readonly orga: OrgaResource;
|
|
30
|
+
readonly cache: CacheBackend;
|
|
31
|
+
readonly logger: Logger;
|
|
32
|
+
readonly mode: 'prod' | 'test';
|
|
33
|
+
private readonly http;
|
|
34
|
+
constructor(options: EBrocanteClientOptions);
|
|
35
|
+
private buildCache;
|
|
36
|
+
private buildLogger;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=EBrocanteClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EBrocanteClient.d.ts","sourceRoot":"","sources":["../src/EBrocanteClient.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAO5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAQzD;;;;;;;;;GASG;AACH,qBAAa,eAAe;IACxB,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAe;IAE9C,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAE/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;gBAEtB,OAAO,EAAE,sBAAsB;IAkC3C,OAAO,CAAC,UAAU;IAmBlB,OAAO,CAAC,WAAW;CAKtB"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* e-brocante JavaScript / TypeScript SDK
|
|
3
|
+
*
|
|
4
|
+
* @copyright 2026 Association E-Cosplay
|
|
5
|
+
* @author Association E-Cosplay <contact@e-cosplay.fr>
|
|
6
|
+
* @license Proprietary — see LICENSE
|
|
7
|
+
*
|
|
8
|
+
* Redistribution and modification forbidden without prior written agreement.
|
|
9
|
+
* Contact: contact@e-cosplay.fr
|
|
10
|
+
*/
|
|
11
|
+
import { LocalCache } from './cache/LocalCache.js';
|
|
12
|
+
import { NullCache } from './cache/NullCache.js';
|
|
13
|
+
import { RedisCache } from './cache/RedisCache.js';
|
|
14
|
+
import { HttpClient, SDK_VERSION } from './http/HttpClient.js';
|
|
15
|
+
import { DebugWriter } from './logger/DebugWriter.js';
|
|
16
|
+
import { FileLogger } from './logger/FileLogger.js';
|
|
17
|
+
import { NullLogger } from './logger/NullLogger.js';
|
|
18
|
+
import { EventsResource } from './resources/EventsResource.js';
|
|
19
|
+
import { OrgaResource } from './resources/OrgaResource.js';
|
|
20
|
+
import { isValidEmail } from './internal/safeStrings.js';
|
|
21
|
+
const DEFAULT_BASE_URL = 'https://e-brocante.enum.fr';
|
|
22
|
+
const DEFAULT_TIMEOUT_MS = 10_000;
|
|
23
|
+
const DEFAULT_CACHE_TTL = 300;
|
|
24
|
+
/**
|
|
25
|
+
* Main client for the e-brocante.enum.fr public API.
|
|
26
|
+
*
|
|
27
|
+
* const client = new EBrocanteClient({
|
|
28
|
+
* apiKey: 'eb_prod_…',
|
|
29
|
+
* brocEmail: 'orga@example.com',
|
|
30
|
+
* });
|
|
31
|
+
* const { data: events } = await client.events.list({ city: 'paris' });
|
|
32
|
+
* const orga = await client.orga.get('asso-sakura');
|
|
33
|
+
*/
|
|
34
|
+
export class EBrocanteClient {
|
|
35
|
+
static VERSION = SDK_VERSION;
|
|
36
|
+
events;
|
|
37
|
+
orga;
|
|
38
|
+
cache;
|
|
39
|
+
logger;
|
|
40
|
+
mode;
|
|
41
|
+
http;
|
|
42
|
+
constructor(options) {
|
|
43
|
+
if (!options.apiKey || typeof options.apiKey !== 'string') {
|
|
44
|
+
throw new TypeError('apiKey is required and must be a string');
|
|
45
|
+
}
|
|
46
|
+
if (!options.brocEmail || !isValidEmail(options.brocEmail)) {
|
|
47
|
+
throw new TypeError(`Invalid brocEmail: '${options.brocEmail}'`);
|
|
48
|
+
}
|
|
49
|
+
const mode = options.mode ?? 'prod';
|
|
50
|
+
if (mode !== 'prod' && mode !== 'test') {
|
|
51
|
+
throw new TypeError(`mode must be 'prod' or 'test', got '${String(mode)}'`);
|
|
52
|
+
}
|
|
53
|
+
this.mode = mode;
|
|
54
|
+
this.cache = this.buildCache(options);
|
|
55
|
+
this.logger = this.buildLogger(options);
|
|
56
|
+
const debugWriter = options.debug === true ? new DebugWriter(options.debugFile ?? 'DEBUG.TXT') : null;
|
|
57
|
+
this.http = new HttpClient({
|
|
58
|
+
apiKey: options.apiKey,
|
|
59
|
+
brocEmail: options.brocEmail,
|
|
60
|
+
mode,
|
|
61
|
+
baseUrl: options.baseUrl ?? DEFAULT_BASE_URL,
|
|
62
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
63
|
+
cache: this.cache,
|
|
64
|
+
logger: this.logger,
|
|
65
|
+
debugWriter,
|
|
66
|
+
maxRetries: options.maxRetries ?? 3,
|
|
67
|
+
fetchImpl: options.fetch ?? globalThis.fetch.bind(globalThis),
|
|
68
|
+
});
|
|
69
|
+
this.events = new EventsResource(this.http);
|
|
70
|
+
this.orga = new OrgaResource(this.http);
|
|
71
|
+
}
|
|
72
|
+
buildCache(options) {
|
|
73
|
+
if (options.cacheImpl)
|
|
74
|
+
return options.cacheImpl;
|
|
75
|
+
if (options.cache === false)
|
|
76
|
+
return new NullCache();
|
|
77
|
+
const ttl = options.cacheTtl ?? DEFAULT_CACHE_TTL;
|
|
78
|
+
const mode = options.mode ?? 'prod';
|
|
79
|
+
const keyPrefix = `${options.cacheKeyPrefix ?? 'ebrocante:'}${mode}:`;
|
|
80
|
+
const type = options.cacheType ?? 'local';
|
|
81
|
+
if (type === 'local')
|
|
82
|
+
return new LocalCache(ttl, keyPrefix);
|
|
83
|
+
if (type === 'redis') {
|
|
84
|
+
if (!options.cacheRedis) {
|
|
85
|
+
throw new TypeError("cacheRedis is required when cacheType='redis'");
|
|
86
|
+
}
|
|
87
|
+
return new RedisCache(options.cacheRedis, ttl, keyPrefix);
|
|
88
|
+
}
|
|
89
|
+
throw new TypeError(`cacheType must be 'local' or 'redis', got '${String(type)}'`);
|
|
90
|
+
}
|
|
91
|
+
buildLogger(options) {
|
|
92
|
+
if (options.logger)
|
|
93
|
+
return options.logger;
|
|
94
|
+
if (options.logPath)
|
|
95
|
+
return new FileLogger(options.logPath);
|
|
96
|
+
return new NullLogger();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=EBrocanteClient.js.map
|