apollo-angular-signal 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,63 +1,186 @@
1
1
  # ApolloAngularSignal
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.0.0.
3
+ A lightweight Angular library that converts Apollo GraphQL queries into Angular signals, enabling seamless integration between Apollo Client and Angular's signal-based reactive programming.
4
4
 
5
- ## Code scaffolding
6
-
7
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
5
+ ## Installation
8
6
 
9
7
  ```bash
10
- ng generate component component-name
8
+ npm install apollo-angular-signal
11
9
  ```
12
10
 
13
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
14
-
15
- ```bash
16
- ng generate --help
11
+ ## What It Does
12
+
13
+ This library takes Apollo Angular `ObservableQuery` results and transforms them into Angular signals, providing a more idiomatic way to work with GraphQL data in modern Angular applications.
14
+
15
+ ## Usage
16
+
17
+ ### Basic Query
18
+
19
+ ```typescript
20
+ import { Component, inject } from '@angular/core';
21
+ import { Apollo, gql } from 'apollo-angular';
22
+ import { gqlQuery } from 'apollo-angular-signal';
23
+
24
+ const GET_USERS = gql`
25
+ query GetUsers {
26
+ users {
27
+ id
28
+ name
29
+ email
30
+ }
31
+ }
32
+ `;
33
+
34
+ @Component({
35
+ selector: 'app-users',
36
+ template: `
37
+ @if (users().loading) {
38
+ <div>Loading...</div>
39
+ }
40
+ @if (users().hasError) {
41
+ <div>Error: {{ users().error }}</div>
42
+ }
43
+ @if (users().data) {
44
+ <ul>
45
+ @for (user of users().data.users; track user.id) {
46
+ <li>{{ user.name }} - {{ user.email }}</li>
47
+ }
48
+ </ul>
49
+ }
50
+ `
51
+ })
52
+ export class UsersComponent {
53
+ private apollo = inject(Apollo);
54
+
55
+ users = gqlQuery<{ users: Array<{ id: string; name: string; email: string }> }>(
56
+ this.apollo.watchQuery({
57
+ query: GET_USERS
58
+ }).valueChanges
59
+ );
60
+ }
17
61
  ```
18
62
 
19
- ## Building
20
-
21
- To build the library, run:
22
-
23
- ```bash
24
- ng build apollo-angular-signal
63
+ ### Query with Variables
64
+
65
+ ```typescript
66
+ import { Component, inject, signal } from '@angular/core';
67
+ import { Apollo, gql } from 'apollo-angular';
68
+ import { gqlQuery } from 'apollo-angular-signal';
69
+
70
+ const GET_USER = gql`
71
+ query GetUser($id: ID!) {
72
+ user(id: $id) {
73
+ id
74
+ name
75
+ email
76
+ }
77
+ }
78
+ `;
79
+
80
+ @Component({
81
+ selector: 'app-user-detail',
82
+ template: `
83
+ <input [(ngModel)]="userId" placeholder="Enter user ID">
84
+
85
+ @if (user().loading) {
86
+ <div>Loading...</div>
87
+ }
88
+ @if (user().data) {
89
+ <div>
90
+ <h2>{{ user().data.user.name }}</h2>
91
+ <p>{{ user().data.user.email }}</p>
92
+ </div>
93
+ }
94
+ `
95
+ })
96
+ export class UserDetailComponent {
97
+ private apollo = inject(Apollo);
98
+
99
+ userId = signal('1');
100
+
101
+ // Reactive query that re-executes when userId changes
102
+ user = gqlQuery<{ user: { id: string; name: string; email: string } }>(() => {
103
+ const id = this.userId();
104
+ if (!id) return null;
105
+
106
+ return this.apollo.watchQuery({
107
+ query: GET_USER,
108
+ variables: { id }
109
+ }).valueChanges;
110
+ });
111
+ }
25
112
  ```
26
113
 
27
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
114
+ ### Subscriptions
115
+
116
+ ```typescript
117
+ import { Component, inject } from '@angular/core';
118
+ import { Apollo, gql } from 'apollo-angular';
119
+ import { gqlQuery } from 'apollo-angular-signal';
120
+
121
+ const MESSAGE_SUBSCRIPTION = gql`
122
+ subscription OnMessageAdded {
123
+ messageAdded {
124
+ id
125
+ text
126
+ author
127
+ }
128
+ }
129
+ `;
130
+
131
+ @Component({
132
+ selector: 'app-messages',
133
+ template: `
134
+ @if (messages().data) {
135
+ <div>
136
+ <p><strong>{{ messages().data.messageAdded.author }}:</strong></p>
137
+ <p>{{ messages().data.messageAdded.text }}</p>
138
+ </div>
139
+ }
140
+ `
141
+ })
142
+ export class MessagesComponent {
143
+ private apollo = inject(Apollo);
144
+
145
+ messages = gqlQuery<{ messageAdded: { id: string; text: string; author: string } }>(
146
+ this.apollo.subscribe({
147
+ query: MESSAGE_SUBSCRIPTION
148
+ })
149
+ );
150
+ }
151
+ ```
28
152
 
29
- ### Publishing the Library
153
+ ## API
30
154
 
31
- Once the project is built, you can publish your library by following these steps:
155
+ ### `gqlQuery<T>(query)`
32
156
 
33
- 1. Navigate to the `dist` directory:
34
- ```bash
35
- cd dist/apollo-angular-signal
36
- ```
157
+ Converts an Apollo query/subscription observable into an Angular signal.
37
158
 
38
- 2. Run the `npm publish` command to publish your library to the npm registry:
39
- ```bash
40
- npm publish
41
- ```
159
+ **Parameters:**
160
+ - `query`: Either an `Observable<QueryResult<T>>` or a function returning one (for reactive queries)
42
161
 
43
- ## Running unit tests
162
+ **Returns:**
163
+ A `Signal<LibResult<T>>` where `LibResult` contains:
164
+ - `data?: T` - The query result data
165
+ - `loading: boolean` - Loading state
166
+ - `hasError: boolean` - Whether an error occurred
167
+ - `error?: unknown` - Error object if present
44
168
 
45
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
169
+ **Two modes:**
46
170
 
47
- ```bash
48
- ng test
171
+ 1. **Static mode**: Pass observable directly
172
+ ```typescript
173
+ gqlQuery(apollo.watchQuery({ query: GET_DATA }).valueChanges)
49
174
  ```
50
175
 
51
- ## Running end-to-end tests
52
-
53
- For end-to-end (e2e) testing, run:
54
-
55
- ```bash
56
- ng e2e
176
+ 2. **Reactive mode**: Pass a function for reactive re-execution
177
+ ```typescript
178
+ gqlQuery(() => {
179
+ const id = someSignal();
180
+ return apollo.watchQuery({ query: GET_DATA, variables: { id } }).valueChanges;
181
+ })
57
182
  ```
58
183
 
59
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
60
-
61
- ## Additional Resources
184
+ ## License
62
185
 
63
- For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
186
+ MIT
@@ -40,7 +40,7 @@ function gqlAsync(fn) {
40
40
  const observable = source$();
41
41
  let sub;
42
42
  if (observable) {
43
- sub = observable.pipe(takeUntilDestroyed()).subscribe({
43
+ sub = observable.subscribe({
44
44
  next: (res) => {
45
45
  state.set({
46
46
  data: res.data,
@@ -1 +1 @@
1
- {"version":3,"file":"apollo-angular-signal.mjs","sources":["../../../projects/apollo-angular-signal/src/lib/apollo-angular-signal.ts","../../../projects/apollo-angular-signal/src/public-api.ts","../../../projects/apollo-angular-signal/src/apollo-angular-signal.ts"],"sourcesContent":["import { computed, effect, signal, type Signal } from '@angular/core';\nimport { ObservableQuery } from '@apollo/client';\nimport type { Apollo } from 'apollo-angular';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport type { Subscription, Observable } from 'rxjs';\n\nexport type GqlQueryResult<T> =\n | Apollo.QueryResult<T>\n | Apollo.SubscribeResult<T>\n | ObservableQuery.Result<T>;\n\nexport type ObservableResult<T> = Observable<GqlQueryResult<T>>;\n\ntype Maybe<T> = T | null | undefined;\n\nexport interface GqlSignalResult<T> {\n data?: T;\n loading: boolean;\n hasError: boolean;\n error?: unknown;\n}\n\nexport function gqlQuery<T>(\n query: ObservableResult<T> | (() => Maybe<ObservableResult<T>>),\n): Signal<GqlSignalResult<T>> {\n if (typeof query === 'function') {\n return gqlAsync(query);\n } else {\n const state = signal<GqlSignalResult<T>>({\n loading: true,\n hasError: false,\n });\n\n query.pipe(takeUntilDestroyed()).subscribe({\n next: (res) => {\n state.set({\n data: res.data as T,\n hasError: !!res.error,\n error: res.error,\n loading: 'loading' in res ? res.loading : false,\n });\n },\n error: (error: unknown) => {\n state.set({\n loading: false,\n hasError: true,\n error,\n });\n },\n });\n\n return state;\n }\n}\n\nfunction gqlAsync<T>(\n fn: () => Maybe<ObservableResult<T>>,\n): Signal<GqlSignalResult<T>> {\n const state = signal<GqlSignalResult<T>>({\n loading: true,\n hasError: false,\n });\n\n const source$ = computed(fn);\n\n effect((onCleanup) => {\n const observable = source$();\n let sub: Maybe<Subscription>;\n if (observable) {\n sub = observable.pipe(takeUntilDestroyed()).subscribe({\n next: (res) => {\n state.set({\n data: res.data as T,\n loading: 'loading' in res ? res.loading : false,\n hasError: !!res.error,\n error: res.error,\n });\n },\n error: (error: unknown) => {\n state.set({\n loading: false,\n hasError: true,\n error,\n });\n },\n });\n }\n\n onCleanup(() => {\n sub?.unsubscribe();\n });\n });\n\n return state;\n}\n","/*\n * Public API Surface of apollo-angular-signal\n */\n\nexport * from './lib/apollo-angular-signal';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;AAsBM,SAAU,QAAQ,CACtB,KAA+D,EAAA;AAE/D,IAAA,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE;AAC/B,QAAA,OAAO,QAAQ,CAAC,KAAK,CAAC;IACxB;SAAO;QACL,MAAM,KAAK,GAAG,MAAM,CAAqB;AACvC,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,QAAQ,EAAE,KAAK;AAChB,SAAA,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,OAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;QAEF,KAAK,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,SAAS,CAAC;AACzC,YAAA,IAAI,EAAE,CAAC,GAAG,KAAI;gBACZ,KAAK,CAAC,GAAG,CAAC;oBACR,IAAI,EAAE,GAAG,CAAC,IAAS;AACnB,oBAAA,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK;oBACrB,KAAK,EAAE,GAAG,CAAC,KAAK;AAChB,oBAAA,OAAO,EAAE,SAAS,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,KAAK;AAChD,iBAAA,CAAC;YACJ,CAAC;AACD,YAAA,KAAK,EAAE,CAAC,KAAc,KAAI;gBACxB,KAAK,CAAC,GAAG,CAAC;AACR,oBAAA,OAAO,EAAE,KAAK;AACd,oBAAA,QAAQ,EAAE,IAAI;oBACd,KAAK;AACN,iBAAA,CAAC;YACJ,CAAC;AACF,SAAA,CAAC;AAEF,QAAA,OAAO,KAAK;IACd;AACF;AAEA,SAAS,QAAQ,CACf,EAAoC,EAAA;IAEpC,MAAM,KAAK,GAAG,MAAM,CAAqB;AACvC,QAAA,OAAO,EAAE,IAAI;AACb,QAAA,QAAQ,EAAE,KAAK;AAChB,KAAA,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,OAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAEF,IAAA,MAAM,OAAO,GAAG,QAAQ,CAAC,EAAE,mDAAC;AAE5B,IAAA,MAAM,CAAC,CAAC,SAAS,KAAI;AACnB,QAAA,MAAM,UAAU,GAAG,OAAO,EAAE;AAC5B,QAAA,IAAI,GAAwB;QAC5B,IAAI,UAAU,EAAE;YACd,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,SAAS,CAAC;AACpD,gBAAA,IAAI,EAAE,CAAC,GAAG,KAAI;oBACZ,KAAK,CAAC,GAAG,CAAC;wBACR,IAAI,EAAE,GAAG,CAAC,IAAS;AACnB,wBAAA,OAAO,EAAE,SAAS,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,KAAK;AAC/C,wBAAA,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK;wBACrB,KAAK,EAAE,GAAG,CAAC,KAAK;AACjB,qBAAA,CAAC;gBACJ,CAAC;AACD,gBAAA,KAAK,EAAE,CAAC,KAAc,KAAI;oBACxB,KAAK,CAAC,GAAG,CAAC;AACR,wBAAA,OAAO,EAAE,KAAK;AACd,wBAAA,QAAQ,EAAE,IAAI;wBACd,KAAK;AACN,qBAAA,CAAC;gBACJ,CAAC;AACF,aAAA,CAAC;QACJ;QAEA,SAAS,CAAC,MAAK;YACb,GAAG,EAAE,WAAW,EAAE;AACpB,QAAA,CAAC,CAAC;AACJ,IAAA,CAAC,CAAC;AAEF,IAAA,OAAO,KAAK;AACd;;AC9FA;;AAEG;;ACFH;;AAEG;;;;"}
1
+ {"version":3,"file":"apollo-angular-signal.mjs","sources":["../../../projects/apollo-angular-signal/src/lib/apollo-angular-signal.ts","../../../projects/apollo-angular-signal/src/public-api.ts","../../../projects/apollo-angular-signal/src/apollo-angular-signal.ts"],"sourcesContent":["import { computed, effect, signal, type Signal } from '@angular/core';\nimport type { ObservableQuery } from '@apollo/client';\nimport type { Apollo } from 'apollo-angular';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport type { Subscription, Observable } from 'rxjs';\n\nexport type GqlQueryResult<T> =\n | Apollo.QueryResult<T>\n | Apollo.SubscribeResult<T>\n | ObservableQuery.Result<T>;\n\nexport type ObservableResult<T> = Observable<GqlQueryResult<T>>;\n\ntype Maybe<T> = T | null | undefined;\n\ninterface LibResult<T> {\n data?: T;\n loading: boolean;\n hasError: boolean;\n error?: unknown;\n}\n\nexport function gqlQuery<T>(\n query: ObservableResult<T> | (() => Maybe<ObservableResult<T>>),\n): Signal<LibResult<T>> {\n if (typeof query === 'function') {\n return gqlAsync(query);\n } else {\n const state = signal<LibResult<T>>({\n loading: true,\n hasError: false,\n });\n\n query.pipe(takeUntilDestroyed()).subscribe({\n next: (res) => {\n state.set({\n data: res.data as T,\n hasError: !!res.error,\n error: res.error,\n loading: 'loading' in res ? res.loading : false,\n });\n },\n error: (error: unknown) => {\n state.set({\n loading: false,\n hasError: true,\n error,\n });\n },\n });\n\n return state;\n }\n}\n\nfunction gqlAsync<T>(\n fn: () => Maybe<ObservableResult<T>>,\n): Signal<LibResult<T>> {\n const state = signal<LibResult<T>>({\n loading: true,\n hasError: false,\n });\n\n const source$ = computed(fn);\n\n effect((onCleanup) => {\n const observable = source$();\n let sub: Maybe<Subscription>;\n if (observable) {\n sub = observable.subscribe({\n next: (res) => {\n state.set({\n data: res.data as T,\n loading: 'loading' in res ? res.loading : false,\n hasError: !!res.error,\n error: res.error,\n });\n },\n error: (error: unknown) => {\n state.set({\n loading: false,\n hasError: true,\n error,\n });\n },\n });\n }\n\n onCleanup(() => {\n sub?.unsubscribe();\n });\n });\n\n return state;\n}\n","/*\n * Public API Surface of apollo-angular-signal\n */\n\nexport * from './lib/apollo-angular-signal';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;AAsBM,SAAU,QAAQ,CACtB,KAA+D,EAAA;AAE/D,IAAA,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE;AAC/B,QAAA,OAAO,QAAQ,CAAC,KAAK,CAAC;IACxB;SAAO;QACL,MAAM,KAAK,GAAG,MAAM,CAAe;AACjC,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,QAAQ,EAAE,KAAK;AAChB,SAAA,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,OAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;QAEF,KAAK,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,SAAS,CAAC;AACzC,YAAA,IAAI,EAAE,CAAC,GAAG,KAAI;gBACZ,KAAK,CAAC,GAAG,CAAC;oBACR,IAAI,EAAE,GAAG,CAAC,IAAS;AACnB,oBAAA,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK;oBACrB,KAAK,EAAE,GAAG,CAAC,KAAK;AAChB,oBAAA,OAAO,EAAE,SAAS,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,KAAK;AAChD,iBAAA,CAAC;YACJ,CAAC;AACD,YAAA,KAAK,EAAE,CAAC,KAAc,KAAI;gBACxB,KAAK,CAAC,GAAG,CAAC;AACR,oBAAA,OAAO,EAAE,KAAK;AACd,oBAAA,QAAQ,EAAE,IAAI;oBACd,KAAK;AACN,iBAAA,CAAC;YACJ,CAAC;AACF,SAAA,CAAC;AAEF,QAAA,OAAO,KAAK;IACd;AACF;AAEA,SAAS,QAAQ,CACf,EAAoC,EAAA;IAEpC,MAAM,KAAK,GAAG,MAAM,CAAe;AACjC,QAAA,OAAO,EAAE,IAAI;AACb,QAAA,QAAQ,EAAE,KAAK;AAChB,KAAA,EAAA,IAAA,SAAA,GAAA,CAAA,EAAA,SAAA,EAAA,OAAA,EAAA,CAAA,GAAA,EAAA,CAAA,CAAC;AAEF,IAAA,MAAM,OAAO,GAAG,QAAQ,CAAC,EAAE,mDAAC;AAE5B,IAAA,MAAM,CAAC,CAAC,SAAS,KAAI;AACnB,QAAA,MAAM,UAAU,GAAG,OAAO,EAAE;AAC5B,QAAA,IAAI,GAAwB;QAC5B,IAAI,UAAU,EAAE;AACd,YAAA,GAAG,GAAG,UAAU,CAAC,SAAS,CAAC;AACzB,gBAAA,IAAI,EAAE,CAAC,GAAG,KAAI;oBACZ,KAAK,CAAC,GAAG,CAAC;wBACR,IAAI,EAAE,GAAG,CAAC,IAAS;AACnB,wBAAA,OAAO,EAAE,SAAS,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,GAAG,KAAK;AAC/C,wBAAA,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK;wBACrB,KAAK,EAAE,GAAG,CAAC,KAAK;AACjB,qBAAA,CAAC;gBACJ,CAAC;AACD,gBAAA,KAAK,EAAE,CAAC,KAAc,KAAI;oBACxB,KAAK,CAAC,GAAG,CAAC;AACR,wBAAA,OAAO,EAAE,KAAK;AACd,wBAAA,QAAQ,EAAE,IAAI;wBACd,KAAK;AACN,qBAAA,CAAC;gBACJ,CAAC;AACF,aAAA,CAAC;QACJ;QAEA,SAAS,CAAC,MAAK;YACb,GAAG,EAAE,WAAW,EAAE;AACpB,QAAA,CAAC,CAAC;AACJ,IAAA,CAAC,CAAC;AAEF,IAAA,OAAO,KAAK;AACd;;AC9FA;;AAEG;;ACFH;;AAEG;;;;"}
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "apollo-angular-signal",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^20 || ^21",
6
6
  "@angular/core": "^20 || ^21",
7
7
  "apollo-angular": "^13",
8
+ "@apollo/client": "^4",
8
9
  "rxjs": "^7"
9
10
  },
10
11
  "sideEffects": false,
@@ -6,13 +6,13 @@ import { Observable } from 'rxjs';
6
6
  type GqlQueryResult<T> = Apollo.QueryResult<T> | Apollo.SubscribeResult<T> | ObservableQuery.Result<T>;
7
7
  type ObservableResult<T> = Observable<GqlQueryResult<T>>;
8
8
  type Maybe<T> = T | null | undefined;
9
- interface GqlSignalResult<T> {
9
+ interface LibResult<T> {
10
10
  data?: T;
11
11
  loading: boolean;
12
12
  hasError: boolean;
13
13
  error?: unknown;
14
14
  }
15
- declare function gqlQuery<T>(query: ObservableResult<T> | (() => Maybe<ObservableResult<T>>)): Signal<GqlSignalResult<T>>;
15
+ declare function gqlQuery<T>(query: ObservableResult<T> | (() => Maybe<ObservableResult<T>>)): Signal<LibResult<T>>;
16
16
 
17
17
  export { gqlQuery };
18
- export type { GqlQueryResult, GqlSignalResult, ObservableResult };
18
+ export type { GqlQueryResult, ObservableResult };