@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.
Files changed (110) hide show
  1. package/AGENTS.md +141 -0
  2. package/CHANGELOG.md +76 -0
  3. package/LICENSE +64 -0
  4. package/README.md +246 -0
  5. package/dist/EBrocanteClient.d.ts +38 -0
  6. package/dist/EBrocanteClient.d.ts.map +1 -0
  7. package/dist/EBrocanteClient.js +99 -0
  8. package/dist/EBrocanteClient.js.map +1 -0
  9. package/dist/cache/CacheBackend.d.ts +20 -0
  10. package/dist/cache/CacheBackend.d.ts.map +1 -0
  11. package/dist/cache/CacheBackend.js +6 -0
  12. package/dist/cache/CacheBackend.js.map +1 -0
  13. package/dist/cache/LocalCache.d.ts +22 -0
  14. package/dist/cache/LocalCache.d.ts.map +1 -0
  15. package/dist/cache/LocalCache.js +53 -0
  16. package/dist/cache/LocalCache.js.map +1 -0
  17. package/dist/cache/NullCache.d.ts +17 -0
  18. package/dist/cache/NullCache.d.ts.map +1 -0
  19. package/dist/cache/NullCache.js +26 -0
  20. package/dist/cache/NullCache.js.map +1 -0
  21. package/dist/cache/RedisCache.d.ts +39 -0
  22. package/dist/cache/RedisCache.d.ts.map +1 -0
  23. package/dist/cache/RedisCache.js +104 -0
  24. package/dist/cache/RedisCache.js.map +1 -0
  25. package/dist/cache/RedisConfig.d.ts +17 -0
  26. package/dist/cache/RedisConfig.d.ts.map +1 -0
  27. package/dist/cache/RedisConfig.js +13 -0
  28. package/dist/cache/RedisConfig.js.map +1 -0
  29. package/dist/cache/stableHashKey.d.ts +11 -0
  30. package/dist/cache/stableHashKey.d.ts.map +1 -0
  31. package/dist/cache/stableHashKey.js +27 -0
  32. package/dist/cache/stableHashKey.js.map +1 -0
  33. package/dist/exceptions/index.d.ts +41 -0
  34. package/dist/exceptions/index.d.ts.map +1 -0
  35. package/dist/exceptions/index.js +62 -0
  36. package/dist/exceptions/index.js.map +1 -0
  37. package/dist/http/HttpClient.d.ts +41 -0
  38. package/dist/http/HttpClient.d.ts.map +1 -0
  39. package/dist/http/HttpClient.js +222 -0
  40. package/dist/http/HttpClient.js.map +1 -0
  41. package/dist/index.d.ts +22 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +17 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/internal/safeStrings.d.ts +52 -0
  46. package/dist/internal/safeStrings.d.ts.map +1 -0
  47. package/dist/internal/safeStrings.js +156 -0
  48. package/dist/internal/safeStrings.js.map +1 -0
  49. package/dist/logger/DebugWriter.d.ts +24 -0
  50. package/dist/logger/DebugWriter.d.ts.map +1 -0
  51. package/dist/logger/DebugWriter.js +62 -0
  52. package/dist/logger/DebugWriter.js.map +1 -0
  53. package/dist/logger/FileLogger.d.ts +26 -0
  54. package/dist/logger/FileLogger.d.ts.map +1 -0
  55. package/dist/logger/FileLogger.js +96 -0
  56. package/dist/logger/FileLogger.js.map +1 -0
  57. package/dist/logger/Logger.d.ts +12 -0
  58. package/dist/logger/Logger.d.ts.map +1 -0
  59. package/dist/logger/Logger.js +6 -0
  60. package/dist/logger/Logger.js.map +1 -0
  61. package/dist/logger/NullLogger.d.ts +13 -0
  62. package/dist/logger/NullLogger.d.ts.map +1 -0
  63. package/dist/logger/NullLogger.js +20 -0
  64. package/dist/logger/NullLogger.js.map +1 -0
  65. package/dist/models/Event.d.ts +37 -0
  66. package/dist/models/Event.d.ts.map +1 -0
  67. package/dist/models/Event.js +42 -0
  68. package/dist/models/Event.js.map +1 -0
  69. package/dist/models/Orga.d.ts +33 -0
  70. package/dist/models/Orga.d.ts.map +1 -0
  71. package/dist/models/Orga.js +39 -0
  72. package/dist/models/Orga.js.map +1 -0
  73. package/dist/models/PaginatedResult.d.ts +27 -0
  74. package/dist/models/PaginatedResult.d.ts.map +1 -0
  75. package/dist/models/PaginatedResult.js +46 -0
  76. package/dist/models/PaginatedResult.js.map +1 -0
  77. package/dist/resources/EventsResource.d.ts +20 -0
  78. package/dist/resources/EventsResource.d.ts.map +1 -0
  79. package/dist/resources/EventsResource.js +46 -0
  80. package/dist/resources/EventsResource.js.map +1 -0
  81. package/dist/resources/OrgaResource.d.ts +20 -0
  82. package/dist/resources/OrgaResource.d.ts.map +1 -0
  83. package/dist/resources/OrgaResource.js +51 -0
  84. package/dist/resources/OrgaResource.js.map +1 -0
  85. package/dist/types.d.ts +81 -0
  86. package/dist/types.d.ts.map +1 -0
  87. package/dist/types.js +8 -0
  88. package/dist/types.js.map +1 -0
  89. package/package.json +72 -0
  90. package/src/EBrocanteClient.ts +110 -0
  91. package/src/cache/CacheBackend.ts +20 -0
  92. package/src/cache/LocalCache.ts +64 -0
  93. package/src/cache/NullCache.ts +32 -0
  94. package/src/cache/RedisCache.ts +123 -0
  95. package/src/cache/RedisConfig.ts +23 -0
  96. package/src/cache/stableHashKey.ts +28 -0
  97. package/src/exceptions/index.ts +75 -0
  98. package/src/http/HttpClient.ts +266 -0
  99. package/src/index.ts +42 -0
  100. package/src/internal/safeStrings.ts +154 -0
  101. package/src/logger/DebugWriter.ts +61 -0
  102. package/src/logger/FileLogger.ts +106 -0
  103. package/src/logger/Logger.ts +13 -0
  104. package/src/logger/NullLogger.ts +22 -0
  105. package/src/models/Event.ts +92 -0
  106. package/src/models/Orga.ts +76 -0
  107. package/src/models/PaginatedResult.ts +62 -0
  108. package/src/resources/EventsResource.ts +48 -0
  109. package/src/resources/OrgaResource.ts +53 -0
  110. 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](https://img.shields.io/badge/license-proprietary-red.svg)](LICENSE)
6
+ [![Node](https://img.shields.io/badge/node-%E2%89%A522-339933.svg)](https://nodejs.org/)
7
+ [![Bun](https://img.shields.io/badge/bun-%E2%89%A51.1-fbf0df.svg)](https://bun.sh/)
8
+ [![TypeScript](https://img.shields.io/badge/typescript-strict-3178C6.svg)](https://www.typescriptlang.org/)
9
+ [![API](https://img.shields.io/badge/API-v1-d97706.svg)](https://e-brocante.enum.fr/api/doc)
10
+
11
+ ### Code quality
12
+
13
+ [![Quality Gate Status](https://sn.e-cosplay.fr/api/project_badges/measure?project=e-brocante-js&metric=alert_status&token=sqb_1f2640dc563aa40f73924fd10ff275036e68e520)](https://sn.e-cosplay.fr/dashboard?id=e-brocante-js)
14
+ [![Coverage](https://sn.e-cosplay.fr/api/project_badges/measure?project=e-brocante-js&metric=coverage&token=sqb_1f2640dc563aa40f73924fd10ff275036e68e520)](https://sn.e-cosplay.fr/dashboard?id=e-brocante-js)
15
+ [![Maintainability Rating](https://sn.e-cosplay.fr/api/project_badges/measure?project=e-brocante-js&metric=software_quality_maintainability_rating&token=sqb_1f2640dc563aa40f73924fd10ff275036e68e520)](https://sn.e-cosplay.fr/dashboard?id=e-brocante-js)
16
+ [![Reliability Rating](https://sn.e-cosplay.fr/api/project_badges/measure?project=e-brocante-js&metric=software_quality_reliability_rating&token=sqb_1f2640dc563aa40f73924fd10ff275036e68e520)](https://sn.e-cosplay.fr/dashboard?id=e-brocante-js)
17
+ [![Security Rating](https://sn.e-cosplay.fr/api/project_badges/measure?project=e-brocante-js&metric=software_quality_security_rating&token=sqb_1f2640dc563aa40f73924fd10ff275036e68e520)](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