@buildonspark/spark-sdk 0.3.6 → 0.3.8

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 (103) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/bare/index.cjs +11107 -10443
  3. package/dist/bare/index.d.cts +274 -219
  4. package/dist/bare/index.d.ts +274 -219
  5. package/dist/bare/index.js +11459 -10792
  6. package/dist/{chunk-LIZFXQWK.js → chunk-3LPEQGVJ.js} +1 -1
  7. package/dist/{chunk-J2P3KTQP.js → chunk-5ASXVNTM.js} +1 -1
  8. package/dist/{chunk-FJ7LTA2O.js → chunk-5HU5W56H.js} +40 -213
  9. package/dist/{chunk-XPHYQ2L6.js → chunk-FP2CRVQH.js} +6471 -5813
  10. package/dist/{chunk-EHKP3Y65.js → chunk-FXIESWE6.js} +8 -11
  11. package/dist/{chunk-IC4IUEOS.js → chunk-VFN34EOX.js} +56 -43
  12. package/dist/{chunk-XWLR6G5C.js → chunk-XI6FCNYG.js} +1 -1
  13. package/dist/{client-GOlkXliC.d.ts → client-By-N7oJS.d.ts} +13 -11
  14. package/dist/{client-AHn11NHe.d.cts → client-pNpGP15j.d.cts} +13 -11
  15. package/dist/debug.cjs +11179 -10516
  16. package/dist/debug.d.cts +12 -8
  17. package/dist/debug.d.ts +12 -8
  18. package/dist/debug.js +5 -5
  19. package/dist/graphql/objects/index.d.cts +3 -3
  20. package/dist/graphql/objects/index.d.ts +3 -3
  21. package/dist/index.cjs +7063 -6395
  22. package/dist/index.d.cts +8 -7
  23. package/dist/index.d.ts +8 -7
  24. package/dist/index.js +12 -6
  25. package/dist/index.node.cjs +6598 -6100
  26. package/dist/index.node.d.cts +8 -7
  27. package/dist/index.node.d.ts +8 -7
  28. package/dist/index.node.js +11 -5
  29. package/dist/{logging-CW3kwBaM.d.cts → logging-DMFVY384.d.ts} +10 -4
  30. package/dist/{logging-D7ukPwRA.d.ts → logging-DxLp34Xm.d.cts} +10 -4
  31. package/dist/native/{chunk-X2QXUON7.js → chunk-AFP5QR4O.js} +11 -8
  32. package/dist/native/{index.cjs → index.react-native.cjs} +7143 -6464
  33. package/dist/native/{index.d.cts → index.react-native.d.cts} +110 -54
  34. package/dist/native/{index.d.ts → index.react-native.d.ts} +110 -54
  35. package/dist/native/{index.js → index.react-native.js} +7162 -6484
  36. package/dist/native/{wasm-GKEDPGTM.js → wasm-D4TI35NF.js} +1 -1
  37. package/dist/proto/spark.cjs +56 -43
  38. package/dist/proto/spark.d.cts +1 -1
  39. package/dist/proto/spark.d.ts +1 -1
  40. package/dist/proto/spark.js +1 -1
  41. package/dist/proto/spark_token.d.cts +1 -1
  42. package/dist/proto/spark_token.d.ts +1 -1
  43. package/dist/proto/spark_token.js +2 -2
  44. package/dist/{spark-WA_4wcBr.d.ts → spark-By6yHsrk.d.cts} +31 -6
  45. package/dist/{spark-WA_4wcBr.d.cts → spark-By6yHsrk.d.ts} +31 -6
  46. package/dist/{spark-wallet.browser-Cg4fB-Nm.d.ts → spark-wallet.browser-C1dQknVj.d.ts} +8 -8
  47. package/dist/{spark-wallet.browser-Db7Y95Kt.d.cts → spark-wallet.browser-CNMo3IvO.d.cts} +8 -8
  48. package/dist/{spark-wallet.node-HEG2ahNd.d.cts → spark-wallet.node-BZJhJZKq.d.cts} +9 -31
  49. package/dist/{spark-wallet.node-DB3ZqtJG.d.ts → spark-wallet.node-Og6__NMh.d.ts} +9 -31
  50. package/dist/tests/test-utils.cjs +8835 -8332
  51. package/dist/tests/test-utils.d.cts +8 -6
  52. package/dist/tests/test-utils.d.ts +8 -6
  53. package/dist/tests/test-utils.js +7 -7
  54. package/dist/{token-transactions-B2-BO7Oz.d.ts → token-transactions-C7yefB2S.d.ts} +2 -2
  55. package/dist/{token-transactions-BAN68xwg.d.cts → token-transactions-CLR3rnYi.d.cts} +2 -2
  56. package/dist/types/index.cjs +56 -43
  57. package/dist/types/index.d.cts +2 -2
  58. package/dist/types/index.d.ts +2 -2
  59. package/dist/types/index.js +2 -2
  60. package/dist/{spark-wallet-jwNvWvpK.d.ts → wallet-config-BoyMVa6n.d.ts} +293 -267
  61. package/dist/{spark-wallet-NxG55m7K.d.cts → wallet-config-xom-9UFF.d.cts} +293 -267
  62. package/package.json +16 -6
  63. package/src/graphql/client.ts +6 -9
  64. package/src/graphql/mutations/CompleteCoopExit.ts +1 -1
  65. package/src/graphql/mutations/RequestCoopExit.ts +3 -1
  66. package/src/graphql/mutations/RequestLightningSend.ts +3 -1
  67. package/src/graphql/objects/CompleteCoopExitInput.ts +22 -33
  68. package/src/graphql/objects/RequestCoopExitInput.ts +39 -45
  69. package/src/graphql/objects/RequestLightningSendInput.ts +31 -39
  70. package/src/index.react-native.ts +21 -0
  71. package/src/proto/mock.ts +0 -264
  72. package/src/proto/spark.ts +94 -51
  73. package/src/services/config.ts +7 -2
  74. package/src/services/connection/connection.browser.ts +27 -19
  75. package/src/services/connection/connection.node.ts +79 -24
  76. package/src/services/connection/connection.ts +396 -224
  77. package/src/services/coop-exit.ts +5 -1
  78. package/src/services/lightning.ts +30 -4
  79. package/src/services/signing.ts +137 -0
  80. package/src/services/token-transactions.ts +8 -8
  81. package/src/services/transfer.ts +116 -1
  82. package/src/services/wallet-config.ts +6 -0
  83. package/src/spark-wallet/proto-descriptors.ts +1 -1
  84. package/src/spark-wallet/spark-wallet.browser.ts +0 -1
  85. package/src/spark-wallet/spark-wallet.react-native.ts +5 -3
  86. package/src/spark-wallet/spark-wallet.ts +124 -83
  87. package/src/spark_descriptors.pb +0 -0
  88. package/src/tests/connection.test.ts +537 -0
  89. package/src/tests/integration/connection.test.ts +39 -0
  90. package/src/tests/integration/coop-exit.test.ts +2 -0
  91. package/src/tests/integration/lightning.test.ts +30 -12
  92. package/src/tests/integration/ssp/coop-exit-validation.test.ts +5 -6
  93. package/src/tests/integration/ssp/static_deposit.test.ts +45 -35
  94. package/src/tests/integration/static_deposit.test.ts +11 -10
  95. package/src/tests/integration/transfer.test.ts +13 -1
  96. package/src/tests/isHermeticTest.ts +1 -1
  97. package/src/tests/optimize.test.ts +45 -0
  98. package/src/tests/token-outputs.test.ts +60 -1
  99. package/src/tests/utils/test-faucet.ts +65 -28
  100. package/src/utils/htlc-transactions.ts +224 -0
  101. package/src/utils/optimize.ts +226 -0
  102. package/src/utils/transaction.ts +36 -0
  103. package/src/native/index.ts +0 -21
@@ -7,6 +7,7 @@ import {
7
7
  type ClientFactory as ClientFactoryWeb,
8
8
  } from "nice-grpc-web";
9
9
  import { ClientMiddlewareCall, Metadata } from "nice-grpc-common";
10
+ import type { ClientMiddleware } from "nice-grpc-common";
10
11
  import { retryMiddleware } from "nice-grpc-client-middleware-retry";
11
12
  import { RetryOptions, SparkCallOptions } from "../../types/grpc.js";
12
13
  import { WalletConfigService } from "../config.js";
@@ -26,7 +27,7 @@ export class ConnectionManagerBrowser extends ConnectionManager {
26
27
  this.transport = transport;
27
28
  }
28
29
 
29
- protected async createChannelWithTLS(address: string, certPath?: string) {
30
+ protected async createChannelWithTLS(address: string) {
30
31
  try {
31
32
  return createChannel(address, this.transport);
32
33
  } catch (error) {
@@ -45,9 +46,9 @@ export class ConnectionManagerBrowser extends ConnectionManager {
45
46
  }
46
47
 
47
48
  protected createAuthnMiddleware() {
48
- return async function* (
49
+ return async function* <Req, Res>(
49
50
  this: ConnectionManagerBrowser,
50
- call: ClientMiddlewareCall<any, any>,
51
+ call: ClientMiddlewareCall<Req, Res>,
51
52
  options: SparkCallOptions,
52
53
  ) {
53
54
  const metadata = Metadata(options.metadata)
@@ -55,17 +56,20 @@ export class ConnectionManagerBrowser extends ConnectionManager {
55
56
  .set("X-Grpc-Web", "1")
56
57
  .set("X-Client-Env", clientEnv)
57
58
  .set("Content-Type", "application/grpc-web+proto");
58
- return yield* call.next(call.request, {
59
+ return yield* call.next(call.request as Req, {
59
60
  ...options,
60
61
  metadata,
61
62
  });
62
- }.bind(this);
63
+ }.bind(this) as <Req, Res>(
64
+ call: ClientMiddlewareCall<Req, Res>,
65
+ options: SparkCallOptions,
66
+ ) => AsyncGenerator<Res, Res | void, undefined>;
63
67
  }
64
68
 
65
- protected createMiddleware(address: string, initialAuthToken: string) {
66
- return async function* (
69
+ protected createMiddleware(address: string) {
70
+ return async function* <Req, Res>(
67
71
  this: ConnectionManagerBrowser,
68
- call: ClientMiddlewareCall<any, any>,
72
+ call: ClientMiddlewareCall<Req, Res>,
69
73
  options: SparkCallOptions,
70
74
  ) {
71
75
  const metadata = Metadata(options.metadata)
@@ -75,14 +79,12 @@ export class ConnectionManagerBrowser extends ConnectionManager {
75
79
  .set("Content-Type", "application/grpc-web+proto");
76
80
 
77
81
  try {
78
- return yield* call.next(call.request, {
82
+ const token = await this.authenticate(address);
83
+ return yield* call.next(call.request as Req, {
79
84
  ...options,
80
- metadata: metadata.set(
81
- "Authorization",
82
- `Bearer ${this.clients.get(address)?.authToken || initialAuthToken}`,
83
- ),
85
+ metadata: metadata.set("Authorization", `Bearer ${token}`),
84
86
  });
85
- } catch (error: any) {
87
+ } catch (error: unknown) {
86
88
  return yield* this.handleMiddlewareError(
87
89
  error,
88
90
  address,
@@ -91,17 +93,21 @@ export class ConnectionManagerBrowser extends ConnectionManager {
91
93
  options,
92
94
  );
93
95
  }
94
- }.bind(this);
96
+ }.bind(this) as <Req, Res>(
97
+ call: ClientMiddlewareCall<Req, Res>,
98
+ options: SparkCallOptions,
99
+ ) => AsyncGenerator<Res, Res | void, undefined>;
95
100
  }
96
101
 
97
102
  protected async createGrpcClient<T>(
98
- defintion:
103
+ definition:
99
104
  | SparkAuthnServiceDefinition
100
105
  | SparkServiceDefinition
101
106
  | SparkTokenServiceDefinition,
102
107
  channel: ChannelWeb,
103
108
  withRetries: boolean,
104
- middleware?: any,
109
+ middleware?: ClientMiddleware<RetryOptions, {}>,
110
+ channelKey?: string,
105
111
  ) {
106
112
  let clientFactory: ClientFactoryWeb;
107
113
 
@@ -119,12 +125,14 @@ export class ConnectionManagerBrowser extends ConnectionManager {
119
125
  if (middleware) {
120
126
  clientFactory = clientFactory.use(middleware);
121
127
  }
122
- const client = clientFactory.create(defintion, channel, {
128
+ const client = clientFactory.create(definition, channel, {
123
129
  "*": options,
124
130
  }) as T;
125
131
  return {
126
132
  ...client,
127
- close: undefined,
133
+ close: channelKey
134
+ ? () => ConnectionManager.releaseChannel(channelKey)
135
+ : undefined,
128
136
  };
129
137
  }
130
138
  }
@@ -7,6 +7,7 @@ import {
7
7
  type Channel,
8
8
  } from "nice-grpc";
9
9
  import { ClientMiddlewareCall, Metadata } from "nice-grpc-common";
10
+ import type { ClientMiddleware } from "nice-grpc-common";
10
11
  import { RetryOptions, SparkCallOptions } from "../../types/grpc.js";
11
12
  import { MockServiceClient, MockServiceDefinition } from "../../proto/mock.js";
12
13
  import { SparkServiceDefinition } from "../../proto/spark.js";
@@ -20,6 +21,8 @@ import { clientEnv } from "../../constants.js";
20
21
  import fs from "fs";
21
22
 
22
23
  export class ConnectionManagerNodeJS extends ConnectionManager {
24
+ private certPath: string | null = null;
25
+
23
26
  constructor(config: WalletConfigService) {
24
27
  super(config);
25
28
  }
@@ -29,17 +32,25 @@ export class ConnectionManagerNodeJS extends ConnectionManager {
29
32
  close: () => void;
30
33
  }
31
34
  > {
32
- const channel = await this.createChannelWithTLS(address);
33
-
35
+ const key = this.makeChannelKey(address, false);
36
+ const channel = await ConnectionManager.acquireChannel<Channel>(key, () =>
37
+ this.createChannelWithTLS(address, false),
38
+ );
34
39
  const client = createClient(MockServiceDefinition, channel);
35
- return { ...client, close: () => channel.close() };
40
+ return {
41
+ ...client,
42
+ close: () => ConnectionManager.releaseChannel(key),
43
+ };
36
44
  }
37
45
 
38
- protected async createChannelWithTLS(address: string, certPath?: string) {
46
+ protected async createChannelWithTLS(
47
+ address: string,
48
+ isStreamClientType: boolean = false,
49
+ ) {
39
50
  try {
40
- if (certPath) {
51
+ if (this.certPath) {
41
52
  try {
42
- const cert = fs.readFileSync(certPath);
53
+ const cert = fs.readFileSync(this.certPath);
43
54
  return createChannel(address, ChannelCredentials.createSsl(cert));
44
55
  } catch (error) {
45
56
  console.error("Error reading certificate:", error);
@@ -53,12 +64,13 @@ export class ConnectionManagerNodeJS extends ConnectionManager {
53
64
  }
54
65
  } else {
55
66
  // No cert provided, use insecure SSL for development
56
- return createChannel(
67
+ const ch = createChannel(
57
68
  address,
58
69
  ChannelCredentials.createSsl(null, null, null, {
59
70
  rejectUnauthorized: false,
60
71
  }),
61
72
  );
73
+ return ch;
62
74
  }
63
75
  } catch (error) {
64
76
  console.error("Channel creation error:", error);
@@ -76,26 +88,29 @@ export class ConnectionManagerNodeJS extends ConnectionManager {
76
88
  }
77
89
 
78
90
  protected createAuthnMiddleware() {
79
- return async function* (
91
+ return async function* <Req, Res>(
80
92
  this: ConnectionManagerNodeJS,
81
- call: ClientMiddlewareCall<any, any>,
93
+ call: ClientMiddlewareCall<Req, Res>,
82
94
  options: SparkCallOptions,
83
95
  ) {
84
96
  const metadata = Metadata(options.metadata).set(
85
97
  "X-Client-Env",
86
98
  clientEnv,
87
99
  );
88
- return yield* call.next(call.request, {
100
+ return yield* call.next(call.request as Req, {
89
101
  ...options,
90
102
  metadata,
91
103
  });
92
- }.bind(this);
104
+ }.bind(this) as <Req, Res>(
105
+ call: ClientMiddlewareCall<Req, Res>,
106
+ options: SparkCallOptions,
107
+ ) => AsyncGenerator<Res, Res | void, undefined>;
93
108
  }
94
109
 
95
- protected createMiddleware(address: string, initialAuthToken: string) {
96
- return async function* (
110
+ protected createMiddleware(address: string) {
111
+ return async function* <Req, Res>(
97
112
  this: ConnectionManagerNodeJS,
98
- call: ClientMiddlewareCall<any, any>,
113
+ call: ClientMiddlewareCall<Req, Res>,
99
114
  options: SparkCallOptions,
100
115
  ) {
101
116
  const metadata = Metadata(options.metadata).set(
@@ -103,12 +118,10 @@ export class ConnectionManagerNodeJS extends ConnectionManager {
103
118
  clientEnv,
104
119
  );
105
120
  try {
106
- return yield* call.next(call.request, {
121
+ const token = await this.authenticate(address);
122
+ return yield* call.next(call.request as Req, {
107
123
  ...options,
108
- metadata: metadata.set(
109
- "Authorization",
110
- `Bearer ${this.clients.get(address)?.authToken || initialAuthToken}`,
111
- ),
124
+ metadata: metadata.set("Authorization", `Bearer ${token}`),
112
125
  });
113
126
  } catch (error: unknown) {
114
127
  return yield* this.handleMiddlewareError(
@@ -119,17 +132,21 @@ export class ConnectionManagerNodeJS extends ConnectionManager {
119
132
  options,
120
133
  );
121
134
  }
122
- }.bind(this);
135
+ }.bind(this) as <Req, Res>(
136
+ call: ClientMiddlewareCall<Req, Res>,
137
+ options: SparkCallOptions,
138
+ ) => AsyncGenerator<Res, Res | void, undefined>;
123
139
  }
124
140
 
125
141
  protected async createGrpcClient<T>(
126
- defintion:
142
+ definition:
127
143
  | SparkAuthnServiceDefinition
128
144
  | SparkServiceDefinition
129
145
  | SparkTokenServiceDefinition,
130
146
  channel: Channel,
131
147
  withRetries: boolean,
132
- middleware?: any,
148
+ middleware?: ClientMiddleware<RetryOptions, {}>,
149
+ channelKey?: string,
133
150
  ) {
134
151
  const retryOptions = {
135
152
  retry: true,
@@ -147,12 +164,50 @@ export class ConnectionManagerNodeJS extends ConnectionManager {
147
164
  if (middleware) {
148
165
  clientFactory = clientFactory.use(middleware);
149
166
  }
150
- const client = clientFactory.create(defintion, channel, {
167
+ const client = clientFactory.create(definition, channel, {
151
168
  "*": options,
152
169
  }) as T;
153
170
  return {
154
171
  ...client,
155
- close: channel.close.bind(channel),
172
+ close: channelKey
173
+ ? () => ConnectionManager.releaseChannel(channelKey)
174
+ : channel.close.bind(channel),
156
175
  };
157
176
  }
177
+
178
+ override async subscribeToEvents(address: string, signal: AbortSignal) {
179
+ const stream = super.subscribeToEvents(address, signal);
180
+ const channel = await this.getChannelForClient("stream", address);
181
+
182
+ if (!channel) {
183
+ throw new Error("Failed to get channel for client");
184
+ }
185
+
186
+ // In Node.js, long-lived gRPC streams keep the underlying socket "ref'd",
187
+ // which prevents the process from exiting. To avoid that (e.g. in CLI tools),
188
+ // we manually unref the socket so Node can shut down when nothing else is active.
189
+ //
190
+ // The gRPC client doesn't expose the socket directly, so we dig through
191
+ // internal fields to find it. This is a bit of a hack and may break if the
192
+ // internals change.
193
+ //
194
+ // Since the socket isn't always immediately available, we retry with setTimeout
195
+ // until it shows up.
196
+ const maybeUnref = () => {
197
+ const internalChannel = (channel as any).internalChannel;
198
+ if (
199
+ internalChannel?.currentPicker?.subchannel?.child?.transport?.session
200
+ ?.socket
201
+ ) {
202
+ internalChannel.currentPicker.subchannel.child.transport.session.socket.unref();
203
+ } else {
204
+ setTimeout(maybeUnref, 100);
205
+ }
206
+ };
207
+
208
+ // Only need to unref in Node environments.
209
+ // In the browser and React Native, the runtime handles shutdown when the tab/app closes.
210
+ maybeUnref();
211
+ return stream;
212
+ }
158
213
  }