@push.rocks/smartrust 1.3.2 → 1.4.1

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
@@ -9,9 +9,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
9
9
  ## Install 📦
10
10
 
11
11
  ```bash
12
- npm install @push.rocks/smartrust
13
- # or
14
- pnpm install @push.rocks/smartrust
12
+ pnpm add @push.rocks/smartrust
15
13
  ```
16
14
 
17
15
  ## Overview 🔭
@@ -105,6 +103,57 @@ bridge.on('management:configChanged', (data) => {
105
103
  bridge.kill();
106
104
  ```
107
105
 
106
+ ### Per-Command Deadlines and Cancellation
107
+
108
+ Non-streaming commands accept an optional `IRustBridgeRequestOptions` argument:
109
+
110
+ ```typescript
111
+ import { RustBridgeRequestError } from '@push.rocks/smartrust';
112
+
113
+ const controller = new AbortController();
114
+
115
+ try {
116
+ const metrics = await bridge.sendCommand(
117
+ 'getMetrics',
118
+ {},
119
+ {
120
+ timeoutMs: 2000,
121
+ signal: controller.signal,
122
+ },
123
+ );
124
+ } catch (error) {
125
+ if (error instanceof RustBridgeRequestError) {
126
+ console.error(error.code, error.message);
127
+ }
128
+ }
129
+ ```
130
+
131
+ `timeoutMs` must be a positive safe integer. When it is omitted, the bridge's
132
+ constructor-level `requestTimeoutMs` remains the default. An already-aborted
133
+ signal rejects before a request ID is allocated or anything is written.
134
+
135
+ Timeout and abort only abandon the local TypeScript wait. They do not kill or
136
+ disconnect the shared bridge, late responses are ignored, and later commands
137
+ can continue using the same bridge.
138
+
139
+ > The JSON-lines protocol has no cancellation message. After a command has
140
+ > been written, Rust may still execute it even if the local wait times out or
141
+ > is aborted. Do not blindly retry mutating commands; first establish whether
142
+ > the original command executed or design the command to be idempotent.
143
+
144
+ Timeout, cancellation, transport-write/exit, and Rust response failures use
145
+ `RustBridgeRequestError`.
146
+ Existing message text remains available, while `code` is stable for
147
+ programmatic handling:
148
+
149
+ | Code | Meaning |
150
+ |---|---|
151
+ | `ERR_RUST_BRIDGE_REQUEST_TIMEOUT` | The local request deadline elapsed |
152
+ | `ERR_RUST_BRIDGE_REQUEST_ABORTED` | The caller's signal aborted the local wait |
153
+ | `ERR_RUST_BRIDGE_REQUEST_WRITE` | The transport rejected the request write |
154
+ | `ERR_RUST_BRIDGE_REQUEST_TRANSPORT` | The transport exited, disconnected, or was killed |
155
+ | `ERR_RUST_BRIDGE_REQUEST_RUST_RESPONSE` | Rust returned `success: false` |
156
+
108
157
  ### Socket Mode — Connect to a Running Daemon 🔗
109
158
 
110
159
  When the Rust binary runs as a system service (e.g., via `systemd`, `launchd`, or a Windows Service), use `connect()` to talk to it over a Unix socket or named pipe:
@@ -284,7 +333,7 @@ const bridge = new RustBridge<TMyCommands>({
284
333
 
285
334
  // --- Bridge Options ---
286
335
  cliArgs: ['--management'], // optional: args passed to binary (default: ['--management'])
287
- requestTimeoutMs: 30000, // optional: per-request timeout (default: 30000)
336
+ requestTimeoutMs: 30000, // optional: default request timeout (default: 30000)
288
337
  streamTimeoutMs: 30000, // optional: streaming inactivity timeout (default: requestTimeoutMs)
289
338
  readyTimeoutMs: 10000, // optional: ready event timeout (default: 10000)
290
339
  maxPayloadSize: 50 * 1024 * 1024, // optional: max message size in bytes (default: 50MB)
@@ -488,7 +537,7 @@ fn main() {
488
537
  | `constructor` | `new RustBridge<T>(options: IRustBridgeOptions)` | Create a new bridge instance |
489
538
  | `spawn()` | `Promise<boolean>` | **Stdio mode**: Spawn the binary and wait for ready; returns `false` on failure |
490
539
  | `connect(socketPath, options?)` | `Promise<boolean>` | **Socket mode**: Connect to a running daemon; returns `false` on failure |
491
- | `sendCommand(method, params)` | `Promise<TCommands[K]['result']>` | Send a typed command and await the response |
540
+ | `sendCommand(method, params, options?)` | `Promise<TCommands[K]['result']>` | Send a typed command with an optional per-call timeout or abort signal |
492
541
  | `sendCommandStreaming(method, params)` | `StreamingResponse<TChunk, TResult>` | Send a streaming command; returns immediately |
493
542
  | `kill()` | `void` | Stdio: SIGTERM the process, SIGKILL after 5s. Socket: close the connection (daemon stays alive) |
494
543
  | `running` | `boolean` | Whether the bridge is currently connected and ready |
@@ -541,6 +590,7 @@ fn main() {
541
590
  | Interface / Type | Description |
542
591
  |---|---|
543
592
  | `IRustBridgeOptions` | Full configuration for `RustBridge` |
593
+ | `IRustBridgeRequestOptions` | Per-command options: `{ signal?, timeoutMs? }` |
544
594
  | `IBinaryLocatorOptions` | Configuration for `RustBinaryLocator` |
545
595
  | `ISocketConnectOptions` | Socket connection options (reconnect settings) |
546
596
  | `IRustBridgeLogger` | Logger interface: `{ log(level, message, data?) }` |
@@ -553,10 +603,12 @@ fn main() {
553
603
  | `TCommandMap` | `Record<string, ICommandDefinition>` |
554
604
  | `TStreamingCommandKeys<T>` | Extracts keys from a command map that have a `chunk` field |
555
605
  | `TExtractChunk<T>` | Extracts the chunk type from a streaming command definition |
606
+ | `RustBridgeRequestError` | Request error class with stable `code`, `requestId`, and `method` fields |
607
+ | `TRustBridgeRequestErrorCode` | Union of stable request error code strings |
556
608
 
557
609
  ## License and Legal Information
558
610
 
559
- This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
611
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.
560
612
 
561
613
  **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
562
614
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartrust',
6
- version: '1.3.2',
7
- description: 'a bridge between JS engines and rust'
6
+ version: '1.4.1',
7
+ description: 'A type-safe bridge between JavaScript engines and Rust binaries.'
8
8
  }
@@ -1,10 +1,12 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import { RustBinaryLocator } from './classes.rustbinarylocator.js';
3
+ import { RustBridgeRequestError } from './classes.rustbridgerequesterror.js';
3
4
  import { StreamingResponse } from './classes.streamingresponse.js';
4
5
  import { StdioTransport } from './classes.stdiotransport.js';
5
6
  import { SocketTransport } from './classes.sockettransport.js';
6
7
  import type {
7
8
  IRustBridgeOptions,
9
+ IRustBridgeRequestOptions,
8
10
  IRustBridgeLogger,
9
11
  ISocketConnectOptions,
10
12
  TCommandMap,
@@ -20,6 +22,20 @@ const defaultLogger: IRustBridgeLogger = {
20
22
  log() {},
21
23
  };
22
24
 
25
+ interface IPendingRequest {
26
+ resolve: (value: any) => void;
27
+ reject: (error: Error) => void;
28
+ timer: ReturnType<typeof setTimeout> | null;
29
+ method: string;
30
+ abortSignal?: AbortSignal;
31
+ abortHandler?: () => void;
32
+ streaming?: StreamingResponse<any, any>;
33
+ }
34
+
35
+ type TPendingRequestSettlement =
36
+ | { outcome: 'resolve'; value: any }
37
+ | { outcome: 'reject'; error: Error };
38
+
23
39
  /**
24
40
  * Generic bridge between TypeScript and a Rust binary.
25
41
  * Communicates via JSON-over-stdin/stdout IPC protocol (stdio mode)
@@ -32,12 +48,7 @@ export class RustBridge<TCommands extends TCommandMap = TCommandMap> extends plu
32
48
  private options: Required<Pick<IRustBridgeOptions, 'cliArgs' | 'requestTimeoutMs' | 'readyTimeoutMs' | 'readyEventName' | 'maxPayloadSize'>> & IRustBridgeOptions;
33
49
  private logger: IRustBridgeLogger;
34
50
  private transport: IRustTransport | null = null;
35
- private pendingRequests = new Map<string, {
36
- resolve: (value: any) => void;
37
- reject: (error: Error) => void;
38
- timer: ReturnType<typeof setTimeout>;
39
- streaming?: StreamingResponse<any, any>;
40
- }>();
51
+ private pendingRequests = new Map<string, IPendingRequest>();
41
52
  private requestCounter = 0;
42
53
  private isRunning = false;
43
54
  private binaryPath: string | null = null;
@@ -116,13 +127,13 @@ export class RustBridge<TCommands extends TCommandMap = TCommandMap> extends plu
116
127
 
117
128
  transport.on('close', (...args: any[]) => {
118
129
  this.logger.log('info', `Transport closed`);
119
- this.cleanup();
130
+ this.cleanup(`${this.options.binaryName} process exited`);
120
131
  this.emit('exit', ...args);
121
132
  });
122
133
 
123
134
  transport.on('error', (err: Error) => {
124
135
  this.logger.log('error', `Transport error: ${err.message}`);
125
- this.cleanup();
136
+ this.cleanup(`${this.options.binaryName} process exited`, err);
126
137
  resolve(false);
127
138
  });
128
139
 
@@ -164,11 +175,32 @@ export class RustBridge<TCommands extends TCommandMap = TCommandMap> extends plu
164
175
  public async sendCommand<K extends string & keyof TCommands>(
165
176
  method: K,
166
177
  params: TCommands[K]['params'],
178
+ options?: IRustBridgeRequestOptions,
167
179
  ): Promise<TCommands[K]['result']> {
180
+ const timeoutMs = options?.timeoutMs ?? this.options.requestTimeoutMs;
181
+ if (
182
+ options?.timeoutMs !== undefined
183
+ && (!Number.isSafeInteger(options.timeoutMs) || options.timeoutMs <= 0)
184
+ ) {
185
+ throw new RangeError('RustBridge request timeoutMs must be a positive safe integer');
186
+ }
187
+
188
+ if (options?.signal?.aborted) {
189
+ throw new RustBridgeRequestError(
190
+ `Command '${method}' was aborted before it was sent`,
191
+ {
192
+ code: 'ERR_RUST_BRIDGE_REQUEST_ABORTED',
193
+ method,
194
+ cause: options.signal.reason,
195
+ },
196
+ );
197
+ }
198
+
168
199
  if (!this.transport?.connected || !this.isRunning) {
169
200
  throw new Error(`${this.options.binaryName} bridge is not running`);
170
201
  }
171
202
 
203
+ const transport = this.transport;
172
204
  const id = `req_${++this.requestCounter}`;
173
205
  const request: IManagementRequest = { id, method, params };
174
206
  const json = JSON.stringify(request);
@@ -182,18 +214,76 @@ export class RustBridge<TCommands extends TCommandMap = TCommandMap> extends plu
182
214
  }
183
215
 
184
216
  return new Promise<TCommands[K]['result']>((resolve, reject) => {
185
- const timer = setTimeout(() => {
186
- this.pendingRequests.delete(id);
187
- reject(new Error(`Command '${method}' timed out after ${this.options.requestTimeoutMs}ms`));
188
- }, this.options.requestTimeoutMs);
217
+ const pending: IPendingRequest = {
218
+ resolve,
219
+ reject,
220
+ timer: null,
221
+ method,
222
+ abortSignal: options?.signal,
223
+ };
224
+ this.pendingRequests.set(id, pending);
225
+
226
+ if (options?.signal) {
227
+ pending.abortHandler = () => {
228
+ this.settlePendingRequest(id, {
229
+ outcome: 'reject',
230
+ error: new RustBridgeRequestError(
231
+ `Command '${method}' was aborted`,
232
+ {
233
+ code: 'ERR_RUST_BRIDGE_REQUEST_ABORTED',
234
+ requestId: id,
235
+ method,
236
+ cause: options.signal?.reason,
237
+ },
238
+ ),
239
+ });
240
+ };
241
+ options.signal.addEventListener('abort', pending.abortHandler, { once: true });
242
+ }
189
243
 
190
- this.pendingRequests.set(id, { resolve, reject, timer });
244
+ pending.timer = setTimeout(() => {
245
+ this.settlePendingRequest(id, {
246
+ outcome: 'reject',
247
+ error: new RustBridgeRequestError(
248
+ `Command '${method}' timed out after ${timeoutMs}ms`,
249
+ {
250
+ code: 'ERR_RUST_BRIDGE_REQUEST_TIMEOUT',
251
+ requestId: id,
252
+ method,
253
+ },
254
+ ),
255
+ });
256
+ }, timeoutMs);
191
257
 
192
- this.transport!.write(json + '\n').catch((err) => {
193
- clearTimeout(timer);
194
- this.pendingRequests.delete(id);
195
- reject(new Error(`Failed to write to transport: ${err.message}`));
196
- });
258
+ try {
259
+ void transport.write(json + '\n').catch((err: unknown) => {
260
+ this.settlePendingRequest(id, {
261
+ outcome: 'reject',
262
+ error: new RustBridgeRequestError(
263
+ `Failed to write to transport: ${this.getErrorMessage(err)}`,
264
+ {
265
+ code: 'ERR_RUST_BRIDGE_REQUEST_WRITE',
266
+ requestId: id,
267
+ method,
268
+ cause: err,
269
+ },
270
+ ),
271
+ });
272
+ });
273
+ } catch (err: unknown) {
274
+ this.settlePendingRequest(id, {
275
+ outcome: 'reject',
276
+ error: new RustBridgeRequestError(
277
+ `Failed to write to transport: ${this.getErrorMessage(err)}`,
278
+ {
279
+ code: 'ERR_RUST_BRIDGE_REQUEST_WRITE',
280
+ requestId: id,
281
+ method,
282
+ cause: err,
283
+ },
284
+ ),
285
+ });
286
+ }
197
287
  });
198
288
  }
199
289
 
@@ -227,22 +317,33 @@ export class RustBridge<TCommands extends TCommandMap = TCommandMap> extends plu
227
317
 
228
318
  const timeoutMs = this.options.streamTimeoutMs ?? this.options.requestTimeoutMs;
229
319
  const timer = setTimeout(() => {
230
- this.pendingRequests.delete(id);
231
- streaming.fail(new Error(`Streaming command '${method}' timed out after ${timeoutMs}ms`));
320
+ this.settlePendingRequest(id, {
321
+ outcome: 'reject',
322
+ error: new Error(`Streaming command '${method}' timed out after ${timeoutMs}ms`),
323
+ });
232
324
  }, timeoutMs);
233
325
 
234
326
  this.pendingRequests.set(id, {
235
327
  resolve: (result: any) => streaming.finish(result),
236
328
  reject: (error: Error) => streaming.fail(error),
237
329
  timer,
330
+ method,
238
331
  streaming,
239
332
  });
240
333
 
241
- this.transport!.write(json + '\n').catch((err) => {
242
- clearTimeout(timer);
243
- this.pendingRequests.delete(id);
244
- streaming.fail(new Error(`Failed to write to transport: ${err.message}`));
245
- });
334
+ try {
335
+ void this.transport!.write(json + '\n').catch((err: unknown) => {
336
+ this.settlePendingRequest(id, {
337
+ outcome: 'reject',
338
+ error: new Error(`Failed to write to transport: ${this.getErrorMessage(err)}`),
339
+ });
340
+ });
341
+ } catch (err: unknown) {
342
+ this.settlePendingRequest(id, {
343
+ outcome: 'reject',
344
+ error: new Error(`Failed to write to transport: ${this.getErrorMessage(err)}`),
345
+ });
346
+ }
246
347
 
247
348
  return streaming;
248
349
  }
@@ -259,11 +360,7 @@ export class RustBridge<TCommands extends TCommandMap = TCommandMap> extends plu
259
360
  this.isRunning = false;
260
361
 
261
362
  // Reject pending requests
262
- for (const [, pending] of this.pendingRequests) {
263
- clearTimeout(pending.timer);
264
- pending.reject(new Error(`${this.options.binaryName} process killed`));
265
- }
266
- this.pendingRequests.clear();
363
+ this.rejectAllPendingRequests(`${this.options.binaryName} process killed`);
267
364
 
268
365
  transport.removeAllListeners();
269
366
  transport.disconnect();
@@ -300,11 +397,15 @@ export class RustBridge<TCommands extends TCommandMap = TCommandMap> extends plu
300
397
  const pending = this.pendingRequests.get(parsed.id);
301
398
  if (pending?.streaming) {
302
399
  // Reset inactivity timeout
303
- clearTimeout(pending.timer);
400
+ if (pending.timer) {
401
+ clearTimeout(pending.timer);
402
+ }
304
403
  const timeoutMs = this.options.streamTimeoutMs ?? this.options.requestTimeoutMs;
305
404
  pending.timer = setTimeout(() => {
306
- this.pendingRequests.delete(parsed.id);
307
- pending.reject(new Error(`Streaming command timed out after ${timeoutMs}ms of inactivity`));
405
+ this.settlePendingRequest(parsed.id, {
406
+ outcome: 'reject',
407
+ error: new Error(`Streaming command timed out after ${timeoutMs}ms of inactivity`),
408
+ });
308
409
  }, timeoutMs);
309
410
  pending.streaming.pushChunk(parsed.data);
310
411
  }
@@ -316,26 +417,85 @@ export class RustBridge<TCommands extends TCommandMap = TCommandMap> extends plu
316
417
  const response = parsed as IManagementResponse;
317
418
  const pending = this.pendingRequests.get(response.id);
318
419
  if (pending) {
319
- clearTimeout(pending.timer);
320
- this.pendingRequests.delete(response.id);
321
420
  if (response.success) {
322
- pending.resolve(response.result);
421
+ this.settlePendingRequest(response.id, {
422
+ outcome: 'resolve',
423
+ value: response.result,
424
+ });
323
425
  } else {
324
- pending.reject(new Error(response.error || 'Unknown error from Rust process'));
426
+ const message = response.error || 'Unknown error from Rust process';
427
+ this.settlePendingRequest(response.id, {
428
+ outcome: 'reject',
429
+ error: pending.streaming
430
+ ? new Error(message)
431
+ : new RustBridgeRequestError(
432
+ message,
433
+ {
434
+ code: 'ERR_RUST_BRIDGE_REQUEST_RUST_RESPONSE',
435
+ requestId: response.id,
436
+ method: pending.method,
437
+ },
438
+ ),
439
+ });
325
440
  }
326
441
  }
327
442
  }
328
443
  }
329
444
 
330
- private cleanup(): void {
331
- this.isRunning = false;
332
- this.transport = null;
445
+ private settlePendingRequest(
446
+ id: string,
447
+ settlement: TPendingRequestSettlement,
448
+ ): boolean {
449
+ const pending = this.pendingRequests.get(id);
450
+ if (!pending) {
451
+ return false;
452
+ }
333
453
 
334
- // Reject all pending requests
335
- for (const [, pending] of this.pendingRequests) {
454
+ this.pendingRequests.delete(id);
455
+ if (pending.timer) {
336
456
  clearTimeout(pending.timer);
337
- pending.reject(new Error(`${this.options.binaryName} process exited`));
457
+ pending.timer = null;
458
+ }
459
+ if (pending.abortSignal && pending.abortHandler) {
460
+ pending.abortSignal.removeEventListener('abort', pending.abortHandler);
461
+ pending.abortHandler = undefined;
462
+ }
463
+
464
+ if (settlement.outcome === 'resolve') {
465
+ pending.resolve(settlement.value);
466
+ } else {
467
+ pending.reject(settlement.error);
338
468
  }
339
- this.pendingRequests.clear();
469
+ return true;
470
+ }
471
+
472
+ private rejectAllPendingRequests(message: string, cause?: unknown): void {
473
+ for (const [id, pending] of [...this.pendingRequests]) {
474
+ this.settlePendingRequest(id, {
475
+ outcome: 'reject',
476
+ error: pending.streaming
477
+ ? new Error(message)
478
+ : new RustBridgeRequestError(
479
+ message,
480
+ {
481
+ code: 'ERR_RUST_BRIDGE_REQUEST_TRANSPORT',
482
+ requestId: id,
483
+ method: pending.method,
484
+ cause,
485
+ },
486
+ ),
487
+ });
488
+ }
489
+ }
490
+
491
+ private cleanup(message: string, cause?: unknown): void {
492
+ this.isRunning = false;
493
+ this.transport = null;
494
+
495
+ this.rejectAllPendingRequests(message, cause);
496
+ }
497
+
498
+ private getErrorMessage(error: unknown): string {
499
+ return error instanceof Error ? error.message : String(error);
340
500
  }
341
501
  }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Stable error codes for failures while waiting for a Rust bridge request.
3
+ */
4
+ export type TRustBridgeRequestErrorCode =
5
+ | 'ERR_RUST_BRIDGE_REQUEST_TIMEOUT'
6
+ | 'ERR_RUST_BRIDGE_REQUEST_ABORTED'
7
+ | 'ERR_RUST_BRIDGE_REQUEST_WRITE'
8
+ | 'ERR_RUST_BRIDGE_REQUEST_TRANSPORT'
9
+ | 'ERR_RUST_BRIDGE_REQUEST_RUST_RESPONSE';
10
+
11
+ export interface IRustBridgeRequestErrorOptions {
12
+ code: TRustBridgeRequestErrorCode;
13
+ requestId?: string;
14
+ method?: string;
15
+ cause?: unknown;
16
+ }
17
+
18
+ /**
19
+ * Error raised while preparing or waiting for a Rust bridge request.
20
+ *
21
+ * Existing error messages are preserved while `code` provides a stable way to
22
+ * distinguish timeout, cancellation, transport, write, and Rust response
23
+ * failures.
24
+ */
25
+ export class RustBridgeRequestError extends Error {
26
+ public readonly code: TRustBridgeRequestErrorCode;
27
+ public readonly requestId?: string;
28
+ public readonly method?: string;
29
+
30
+ constructor(message: string, options: IRustBridgeRequestErrorOptions) {
31
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
32
+ this.name = 'RustBridgeRequestError';
33
+ this.code = options.code;
34
+ this.requestId = options.requestId;
35
+ this.method = options.method;
36
+ }
37
+ }
package/ts/index.ts CHANGED
@@ -1,4 +1,9 @@
1
1
  export { RustBridge } from './classes.rustbridge.js';
2
+ export {
3
+ RustBridgeRequestError,
4
+ type IRustBridgeRequestErrorOptions,
5
+ type TRustBridgeRequestErrorCode,
6
+ } from './classes.rustbridgerequesterror.js';
2
7
  export { RustBinaryLocator } from './classes.rustbinarylocator.js';
3
8
  export { StreamingResponse } from './classes.streamingresponse.js';
4
9
  export { StdioTransport } from './classes.stdiotransport.js';
@@ -5,6 +5,16 @@ export interface IRustBridgeLogger {
5
5
  log(level: string, message: string, data?: Record<string, any>): void;
6
6
  }
7
7
 
8
+ /**
9
+ * Options for one non-streaming Rust bridge request.
10
+ */
11
+ export interface IRustBridgeRequestOptions {
12
+ /** Abort only the local wait for this request. */
13
+ signal?: AbortSignal;
14
+ /** Override the bridge request timeout for this call, in milliseconds. */
15
+ timeoutMs?: number;
16
+ }
17
+
8
18
  /**
9
19
  * Options for locating a Rust binary.
10
20
  */
@@ -1,66 +0,0 @@
1
- name: Default (not tags)
2
-
3
- on:
4
- push:
5
- tags-ignore:
6
- - '**'
7
-
8
- env:
9
- IMAGE: code.foss.global/host.today/ht-docker-node:npmci
10
- NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@code.foss.global/${{gitea.repository}}.git
11
- NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
12
- NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
13
- NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}
14
- NPMCI_URL_CLOUDLY: ${{secrets.NPMCI_URL_CLOUDLY}}
15
-
16
- jobs:
17
- security:
18
- runs-on: ubuntu-latest
19
- continue-on-error: true
20
- container:
21
- image: ${{ env.IMAGE }}
22
-
23
- steps:
24
- - uses: actions/checkout@v3
25
-
26
- - name: Install pnpm and npmci
27
- run: |
28
- pnpm install -g pnpm
29
- pnpm install -g @ship.zone/npmci
30
-
31
- - name: Run npm prepare
32
- run: npmci npm prepare
33
-
34
- - name: Audit production dependencies
35
- run: |
36
- npmci command npm config set registry https://registry.npmjs.org
37
- npmci command pnpm audit --audit-level=high --prod
38
- continue-on-error: true
39
-
40
- - name: Audit development dependencies
41
- run: |
42
- npmci command npm config set registry https://registry.npmjs.org
43
- npmci command pnpm audit --audit-level=high --dev
44
- continue-on-error: true
45
-
46
- test:
47
- if: ${{ always() }}
48
- needs: security
49
- runs-on: ubuntu-latest
50
- container:
51
- image: ${{ env.IMAGE }}
52
-
53
- steps:
54
- - uses: actions/checkout@v3
55
-
56
- - name: Test stable
57
- run: |
58
- npmci node install stable
59
- npmci npm install
60
- npmci npm test
61
-
62
- - name: Test build
63
- run: |
64
- npmci node install stable
65
- npmci npm install
66
- npmci npm build