@obsidiane/auth-client-js 1.0.3 → 1.0.5

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 (48) hide show
  1. package/README.md +725 -105
  2. package/fesm2022/obsidiane-auth-client-js.mjs +961 -0
  3. package/fesm2022/obsidiane-auth-client-js.mjs.map +1 -0
  4. package/index.d.ts +361 -0
  5. package/obsidiane-auth-client-js-0.1.0.tgz +0 -0
  6. package/package.json +15 -36
  7. package/DEVELOPMENT.md +0 -226
  8. package/dist/index.js +0 -10
  9. package/index.ts +0 -10
  10. package/src/lib/bridge/rest/api-platform.adapter.ts +0 -84
  11. package/src/lib/bridge/rest/http-request.options.ts +0 -21
  12. package/src/lib/bridge/rest/query-builder.ts +0 -43
  13. package/src/lib/bridge/sse/eventsource-wrapper.ts +0 -70
  14. package/src/lib/bridge/sse/mercure-topic.mapper.ts +0 -48
  15. package/src/lib/bridge/sse/mercure-url.builder.ts +0 -17
  16. package/src/lib/bridge/sse/mercure.adapter.ts +0 -261
  17. package/src/lib/bridge/sse/ref-count-topic.registry.ts +0 -45
  18. package/src/lib/bridge.types.ts +0 -33
  19. package/src/lib/facades/bridge.facade.ts +0 -108
  20. package/src/lib/facades/facade.factory.ts +0 -38
  21. package/src/lib/facades/facade.interface.ts +0 -30
  22. package/src/lib/facades/resource.facade.ts +0 -101
  23. package/src/lib/interceptors/bridge-debug.interceptor.ts +0 -32
  24. package/src/lib/interceptors/bridge-defaults.interceptor.ts +0 -53
  25. package/src/lib/interceptors/content-type.interceptor.ts +0 -49
  26. package/src/lib/interceptors/singleflight.interceptor.ts +0 -55
  27. package/src/lib/ports/realtime.port.ts +0 -36
  28. package/src/lib/ports/resource-repository.port.ts +0 -78
  29. package/src/lib/provide-bridge.ts +0 -148
  30. package/src/lib/tokens.ts +0 -20
  31. package/src/lib/utils/url.ts +0 -15
  32. package/src/models/Auth.ts +0 -5
  33. package/src/models/AuthInviteCompleteInputInviteComplete.ts +0 -7
  34. package/src/models/AuthInviteUserInputInviteSend.ts +0 -5
  35. package/src/models/AuthLdJson.ts +0 -5
  36. package/src/models/AuthPasswordForgotInputPasswordForgot.ts +0 -5
  37. package/src/models/AuthPasswordResetInputPasswordReset.ts +0 -6
  38. package/src/models/AuthRegisterUserInputUserRegister.ts +0 -6
  39. package/src/models/FrontendConfig.ts +0 -12
  40. package/src/models/InvitePreview.ts +0 -8
  41. package/src/models/InviteUserInviteRead.ts +0 -9
  42. package/src/models/Setup.ts +0 -5
  43. package/src/models/SetupRegisterUserInputUserRegister.ts +0 -6
  44. package/src/models/UserUpdateUserRolesInputUserRoles.ts +0 -5
  45. package/src/models/UserUserRead.ts +0 -9
  46. package/src/models/index.ts +0 -14
  47. package/src/public-api.ts +0 -9
  48. package/tsconfig.json +0 -23
package/DEVELOPMENT.md DELETED
@@ -1,226 +0,0 @@
1
- # Development Guide
2
-
3
- ## Architecture Overview
4
-
5
- ```
6
- @obsidiane/auth-client-js
7
- ├── Bridge (Meridiane-generated) ← Auto-generated from OpenAPI
8
- │ ├── src/lib/ ← HTTP client, facades, interceptors
9
- │ ├── src/models/ ← TypeScript types
10
- │ └── src/public-api.ts ← Main exports
11
-
12
- └── Config
13
- ├── package.json ← NPM metadata
14
- ├── tsconfig.json ← TypeScript config
15
- └── .gitignore ← Ignore generated /src/
16
- ```
17
-
18
- ## File Structure
19
-
20
- ### Generated Files (auto-ignored)
21
-
22
- ```
23
- src/ ← GENERATED by Meridiane, ignored in git
24
- ├── lib/
25
- │ ├── bridge/
26
- │ │ ├── rest/ ← HTTP adapters
27
- │ │ └── sse/ ← Realtime (optional)
28
- │ ├── facades/ ← Resource facades
29
- │ ├── interceptors/ ← HTTP interceptors
30
- │ ├── ports/ ← Interfaces
31
- │ ├── utils/ ← Helpers
32
- │ ├── provide-bridge.ts ← Angular provider
33
- │ ├── bridge.types.ts ← Core types
34
- │ └── tokens.ts ← DI tokens
35
- ├── models/ ← Generated from OpenAPI schemas
36
- └── public-api.ts ← Main exports
37
- ```
38
-
39
- ### Manual Files (version-controlled)
40
-
41
- ```
42
- package.json
43
- tsconfig.json
44
- README.md
45
- DEVELOPMENT.md (this file)
46
- .gitignore
47
- ```
48
-
49
- ## Regeneration Workflow
50
-
51
- ### When Backend API Changes
52
-
53
- 1. **Backend deploys** new endpoints to `/api/docs.json`
54
-
55
- 2. **Run generation**:
56
- ```bash
57
- make sdks
58
- ```
59
-
60
- 3. **What happens**:
61
- - Downloads OpenAPI spec from `http://localhost:9000/api/docs.json`
62
- - Runs Meridiane to generate the bridge
63
- - Copies `src/` into `packages/auth-client-js/src/`
64
-
65
- 4. **Commit**:
66
- ```bash
67
- git add packages/auth-client-js/
68
- git commit -m "chore: regenerate SDKs from OpenAPI spec"
69
- ```
70
-
71
- 5. **Publish** (if needed):
72
- ```bash
73
- cd packages/auth-client-js
74
- npm run build
75
- npm publish
76
- ```
77
-
78
- ## Adding New Features
79
-
80
- ### Scenario 1: New HTTP Endpoint
81
-
82
- **The endpoint is added in the backend API** → automatically generated by Meridiane.
83
-
84
- ```bash
85
- make sdk-npm
86
- # Done! New endpoint is in src/ automatically
87
- ```
88
-
89
- ## Building for Publication
90
-
91
- ```bash
92
- # Install dependencies (if needed for TypeScript compilation)
93
- npm install
94
-
95
- # Build TypeScript → JavaScript
96
- npm run build
97
-
98
- # Output in ./dist/
99
- ls -la dist/
100
-
101
- # Publish to npm
102
- npm publish
103
- ```
104
-
105
- ## Using in Applications
106
-
107
- ### In Angular App
108
-
109
- ```typescript
110
- // app.config.ts
111
- import { provideBridge } from '@obsidiane/auth-client-js';
112
-
113
- export const appConfig: ApplicationConfig = {
114
- providers: [
115
- provideBridge({
116
- baseUrl: 'http://localhost:9000',
117
- }),
118
- ],
119
- };
120
- ```
121
-
122
- ### In a Service
123
-
124
- ```typescript
125
- import { FacadeFactory } from '@obsidiane/auth-client-js';
126
- import type { UserUserRead } from '@obsidiane/auth-client-js';
127
-
128
- @Injectable({ providedIn: 'root' })
129
- export class UsersService {
130
- private readonly factory = inject(FacadeFactory);
131
- private readonly facade = this.factory.create<UserUserRead>({
132
- url: '/api/users',
133
- });
134
-
135
- getAll() {
136
- return this.facade.getCollection$();
137
- }
138
-
139
- getOne(id: string) {
140
- return this.facade.get$(id);
141
- }
142
- }
143
- ```
144
-
145
- ## Testing
146
-
147
- The bridge (Meridiane) is tested separately in its own project.
148
-
149
- ## Troubleshooting
150
-
151
- ### Build Fails
152
-
153
- ```bash
154
- npm run build
155
-
156
- # If tsc not found
157
- npm install
158
- npm run build
159
- ```
160
-
161
- ### "Cannot find module 'src/public-api'"
162
-
163
- The `src/` folder doesn't exist. You need to generate it:
164
-
165
- ```bash
166
- make sdk-npm
167
- ```
168
-
169
- ## CI/CD Integration
170
-
171
- Example GitHub Actions workflow:
172
-
173
- ```yaml
174
- name: Publish SDK
175
-
176
- on:
177
- push:
178
- branches: [main]
179
-
180
- jobs:
181
- publish:
182
- runs-on: ubuntu-latest
183
- steps:
184
- - uses: actions/checkout@v3
185
-
186
- # Regenerate from OpenAPI (ensures freshness)
187
- - run: make sdk-npm
188
-
189
- # Build
190
- - run: npm ci
191
- working-directory: packages/auth-client-js
192
- - run: npm run build
193
- working-directory: packages/auth-client-js
194
-
195
- # Publish to npm
196
- - run: npm publish
197
- working-directory: packages/auth-client-js
198
- env:
199
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
200
- ```
201
-
202
- ## Synchronization with webfront/bridge
203
-
204
- Both the SDK and the webfront bridge are generated from the same OpenAPI spec:
205
-
206
- - **webfront/bridge**: Direct use in the Angular app
207
- - **packages/auth-client-js**: Packaged for reuse in other projects
208
-
209
- They should be **identical in functionality** but:
210
- - Bridge: used directly via imports (`import { provideBridge }`)
211
- - SDK: packaged as npm module (`npm install @obsidiane/auth-client-js`)
212
-
213
- Both can coexist in your monorepo.
214
-
215
- ## Maintenance Checklist
216
-
217
- - [ ] Run `make sdks` after API changes
218
- - [ ] Version bump before publishing (in `package.json`)
219
- - [ ] Run `npm run build` before release
220
- - [ ] Commit `package.json` + `package-lock.json` changes
221
-
222
- ## References
223
-
224
- - [Meridiane Documentation](https://github.com/obsidiane-lab/meridiane)
225
- - [Angular HTTP Client](https://angular.io/guide/http)
226
- - [Angular Dependency Injection](https://angular.io/guide/dependency-injection)
package/dist/index.js DELETED
@@ -1,10 +0,0 @@
1
- /**
2
- * Obsidiane Auth Client for Angular/TypeScript
3
- *
4
- * Main entry point that exports the Meridiane-generated bridge:
5
- * - HTTP client (BridgeFacade)
6
- * - Facades (resource-based API)
7
- * - Models (auto-generated types from OpenAPI)
8
- */
9
- export * from './src/public-api';
10
- //# sourceMappingURL=index.js.map
package/index.ts DELETED
@@ -1,10 +0,0 @@
1
- /**
2
- * Obsidiane Auth Client for Angular/TypeScript
3
- *
4
- * Main entry point that exports the Meridiane-generated bridge:
5
- * - HTTP client (BridgeFacade)
6
- * - Facades (resource-based API)
7
- * - Models (auto-generated types from OpenAPI)
8
- */
9
-
10
- export * from './src/public-api';
@@ -1,84 +0,0 @@
1
- import {HttpClient} from '@angular/common/http';
2
- import {Observable} from 'rxjs';
3
- import {toHttpParams} from './query-builder';
4
- import {buildHttpRequestOptions} from './http-request.options';
5
- import {
6
- Collection,
7
- HttpCallOptions,
8
- HttpRequestConfig,
9
- Iri,
10
- IriRequired,
11
- Item,
12
- AnyQuery,
13
- ResourceRepository,
14
- } from '../../ports/resource-repository.port';
15
- import {resolveUrl} from '../../utils/url';
16
-
17
- export class ApiPlatformRestRepository<T extends Item> implements ResourceRepository<T> {
18
- constructor(
19
- private readonly http: HttpClient,
20
- private readonly apiBase: string,
21
- private readonly resourcePath: Iri,
22
- private readonly withCredentialsDefault: boolean,
23
- ) {
24
- }
25
-
26
- getCollection$(query?: AnyQuery, opts?: HttpCallOptions): Observable<Collection<T>> {
27
- const params = toHttpParams(query);
28
- return this.http.get<Collection<T>>(this.resolveUrl(this.resourcePath), {
29
- params,
30
- headers: opts?.headers,
31
- withCredentials: opts?.withCredentials ?? this.withCredentialsDefault,
32
- });
33
- }
34
-
35
- get$(iri: IriRequired, opts?: HttpCallOptions): Observable<T> {
36
- return this.http.get<T>(this.resolveUrl(iri), {
37
- headers: opts?.headers,
38
- withCredentials: opts?.withCredentials ?? this.withCredentialsDefault,
39
- });
40
- }
41
-
42
- post$(payload: Partial<T>, opts?: HttpCallOptions): Observable<T> {
43
- return this.http.post<T>(this.resolveUrl(this.resourcePath), payload, {
44
- headers: opts?.headers,
45
- withCredentials: opts?.withCredentials ?? this.withCredentialsDefault,
46
- });
47
- }
48
-
49
- patch$(iri: IriRequired, changes: Partial<T>, opts?: HttpCallOptions): Observable<T> {
50
- return this.http.patch<T>(this.resolveUrl(iri), changes, {
51
- headers: opts?.headers,
52
- withCredentials: opts?.withCredentials ?? this.withCredentialsDefault,
53
- });
54
- }
55
-
56
- put$(iri: IriRequired, payload: Partial<T>, opts?: HttpCallOptions): Observable<T> {
57
- return this.http.put<T>(this.resolveUrl(iri), payload, {
58
- headers: opts?.headers,
59
- withCredentials: opts?.withCredentials ?? this.withCredentialsDefault,
60
- });
61
- }
62
-
63
- delete$(iri: IriRequired, opts?: HttpCallOptions): Observable<void> {
64
- return this.http.delete<void>(this.resolveUrl(iri), {
65
- headers: opts?.headers,
66
- withCredentials: opts?.withCredentials ?? this.withCredentialsDefault,
67
- });
68
- }
69
-
70
- request$<R = unknown, B = unknown>(req: HttpRequestConfig<B>): Observable<R> {
71
- // Low-level escape hatch for non-standard endpoints (custom controllers, uploads, etc.).
72
- const {method, url} = req;
73
-
74
- const targetUrl = this.resolveUrl(url ?? this.resourcePath);
75
- const mergedOptions = buildHttpRequestOptions(req, {withCredentialsDefault: this.withCredentialsDefault});
76
- return this.http.request<R>(method, targetUrl, mergedOptions as {observe: 'body'});
77
- }
78
-
79
- private resolveUrl(path?: Iri): string {
80
- const effectivePath = path ?? this.resourcePath;
81
- if (!effectivePath) throw new Error('ApiPlatformRestRepository: missing url and resourcePath');
82
- return resolveUrl(this.apiBase, effectivePath);
83
- }
84
- }
@@ -1,21 +0,0 @@
1
- import {HttpRequestConfig} from '../../ports/resource-repository.port';
2
- import {toHttpParams} from './query-builder';
3
-
4
- export function buildHttpRequestOptions(
5
- req: HttpRequestConfig,
6
- {withCredentialsDefault}: {withCredentialsDefault: boolean}
7
- ): Record<string, unknown> {
8
- const {query, body, headers, responseType, withCredentials, options = {}} = req;
9
- const mergedOptions: Record<string, unknown> = {...options};
10
-
11
- if (headers) mergedOptions['headers'] = headers;
12
- if (query) mergedOptions['params'] = toHttpParams(query);
13
- if (body !== undefined) mergedOptions['body'] = body;
14
-
15
- mergedOptions['responseType'] = (responseType ?? (mergedOptions['responseType'] as any) ?? 'json') as any;
16
- mergedOptions['withCredentials'] =
17
- withCredentials ?? (mergedOptions['withCredentials'] as any) ?? withCredentialsDefault;
18
- mergedOptions['observe'] = 'body';
19
-
20
- return mergedOptions;
21
- }
@@ -1,43 +0,0 @@
1
- import {HttpParams} from '@angular/common/http';
2
- import {AnyQuery, Query, QueryParamValue} from '../../ports/resource-repository.port';
3
-
4
- export function toHttpParams(q: AnyQuery | undefined): HttpParams {
5
- if (!q) return new HttpParams();
6
- if (q instanceof HttpParams) return q;
7
-
8
- const fromObject: Record<string, string | string[]> = {};
9
-
10
- const consumed = new Set<string>();
11
- const maybeQuery = q as Query;
12
- if (maybeQuery.page != null) {
13
- fromObject['page'] = String(maybeQuery.page);
14
- consumed.add('page');
15
- }
16
- if (maybeQuery.itemsPerPage != null) {
17
- fromObject['itemsPerPage'] = String(maybeQuery.itemsPerPage);
18
- consumed.add('itemsPerPage');
19
- }
20
-
21
- if (q.filters) {
22
- consumed.add('filters');
23
- for (const [k, v] of Object.entries(q.filters)) {
24
- assign(fromObject, k, v);
25
- }
26
- }
27
-
28
- for (const [k, v] of Object.entries(q as Record<string, QueryParamValue>)) {
29
- if (consumed.has(k)) continue;
30
- assign(fromObject, k, v);
31
- }
32
-
33
- return new HttpParams({fromObject});
34
- }
35
-
36
- function assign(target: Record<string, string | string[]>, key: string, value: QueryParamValue | undefined) {
37
- if (value == null) return;
38
- if (Array.isArray(value)) {
39
- target[key] = value.map(String);
40
- } else {
41
- target[key] = String(value);
42
- }
43
- }
@@ -1,70 +0,0 @@
1
- import {ReplaySubject, Subject} from 'rxjs';
2
- import {SseEvent, SseOptions, RealtimeStatus} from '../../ports/realtime.port';
3
- import {BridgeLogger} from '../../bridge.types';
4
-
5
- export class EventSourceWrapper {
6
- private es?: EventSource;
7
-
8
- private readonly statusSub = new ReplaySubject<RealtimeStatus>(1);
9
- private readonly eventSub = new Subject<SseEvent>();
10
-
11
- readonly status$ = this.statusSub.asObservable();
12
- readonly events$ = this.eventSub.asObservable();
13
-
14
- constructor(
15
- private readonly url: string,
16
- private readonly opts: SseOptions = {},
17
- private readonly logger?: BridgeLogger,
18
- ) {
19
- this.setState('closed');
20
- this.log('[SSE] init', {url, withCredentials: !!opts.withCredentials});
21
- }
22
-
23
- open(): void {
24
- if (this.es) {
25
- this.log('[SSE] open() ignored: already open');
26
- return;
27
- }
28
-
29
- this.setState('connecting');
30
- this.log('[SSE] open', {url: this.url});
31
-
32
- const es = new EventSource(this.url, {
33
- withCredentials: !!this.opts.withCredentials,
34
- });
35
- this.es = es;
36
-
37
- es.onopen = () => {
38
- this.setState('connected');
39
- };
40
-
41
- es.onmessage = (ev) => {
42
- this.eventSub.next({type: 'message', data: ev.data, lastEventId: ev.lastEventId || undefined});
43
- };
44
-
45
- es.onerror = () => {
46
- // The browser will retry automatically. We stay in "connecting".
47
- this.log('[SSE] error');
48
- this.setState('connecting');
49
- };
50
- }
51
-
52
- close(): void {
53
- if (this.es) {
54
- this.es.close();
55
- this.es = undefined;
56
- this.log('[SSE] closed');
57
- }
58
- this.setState('closed');
59
- }
60
-
61
- // ──────────────── internals ────────────────
62
-
63
- private setState(state: RealtimeStatus): void {
64
- this.statusSub.next(state);
65
- }
66
-
67
- private log(...args: unknown[]): void {
68
- this.logger?.debug?.(...args);
69
- }
70
- }
@@ -1,48 +0,0 @@
1
- import {MercureTopicMode} from '../../bridge.types';
2
-
3
- export class MercureTopicMapper {
4
- private readonly apiBaseUrl: URL;
5
-
6
- constructor(
7
- apiBase: string,
8
- private readonly mode: MercureTopicMode,
9
- ) {
10
- this.apiBaseUrl = new URL(apiBase);
11
- }
12
-
13
- /**
14
- * Canonical value used for ref-counting and as the "topic" query param value.
15
- * - mode "url": always absolute, same-origin resolved
16
- * - mode "iri": same-origin path+query+hash ("/api/..."), otherwise keep as-is
17
- */
18
- toTopic(input: string): string {
19
- if (this.mode === 'url') return this.toAbsoluteUrl(input);
20
- return this.toRelativeIriIfSameOrigin(input);
21
- }
22
-
23
- /**
24
- * Canonical value used to compare incoming payload IRIs with subscribed IRIs.
25
- * We keep payload matching stable by using same-origin relative IRIs ("/api/...").
26
- */
27
- toPayloadIri(input: string): string {
28
- return this.toRelativeIriIfSameOrigin(input);
29
- }
30
-
31
- private toAbsoluteUrl(input: string): string {
32
- try {
33
- return new URL(input, this.apiBaseUrl).toString();
34
- } catch {
35
- return input;
36
- }
37
- }
38
-
39
- private toRelativeIriIfSameOrigin(input: string): string {
40
- try {
41
- const url = new URL(input, this.apiBaseUrl);
42
- if (url.origin !== this.apiBaseUrl.origin) return input;
43
- return `${url.pathname}${url.search}${url.hash}`;
44
- } catch {
45
- return input;
46
- }
47
- }
48
- }
@@ -1,17 +0,0 @@
1
- export class MercureUrlBuilder {
2
- /**
3
- * Builds the Mercure hub URL with one `topic=` parameter per topic.
4
- * The adapter is responsible for canonicalising topics beforehand.
5
- */
6
- build(hubUrl: string, topics: ReadonlySet<string>, lastEventId?: string): string {
7
- const url = new URL(hubUrl);
8
- if (lastEventId) {
9
- url.searchParams.set('lastEventID', lastEventId);
10
- }
11
- url.searchParams.delete('topic');
12
- for (const topic of topics) {
13
- url.searchParams.append('topic', topic);
14
- }
15
- return url.toString();
16
- }
17
- }