@perpscope/percolator-adapter 0.9.0 → 1.0.0

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
@@ -72,12 +72,15 @@ The full field-level contract is documented in `../../docs/field-compatibility-m
72
72
  ## CLI
73
73
 
74
74
  ```bash
75
+ perpscope init perpscope.capture.json
75
76
  perpscope compat report capture.json
76
77
  perpscope compat diff previous.json current.json
77
- perpscope compat doctor capture.json
78
+ perpscope compat doctor capture.json --strict
78
79
  perpscope compat badge capture.json --json
79
80
  ```
80
81
 
82
+ Doctor exit codes are CI-ready: `0` means required fields pass, `1` means rejected or required fields missing, and `2` means strict mode found useful-field gaps, unknown fields, or alias suggestions.
83
+
81
84
  Try it locally with:
82
85
 
83
86
  ```bash
package/bin/perpscope.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync } from "node:fs";
2
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import {
4
4
  buildCompatibilityBadge,
5
5
  buildCompatibilityDoctor,
@@ -13,19 +13,104 @@ import {
13
13
  } from "../index.js";
14
14
 
15
15
  const [, , ...args] = process.argv;
16
+ const CAPTURE_TEMPLATE = {
17
+ label: "My terminal read-only capture",
18
+ cluster: "mainnet-beta",
19
+ market: {
20
+ symbol: "SOL-PERP",
21
+ base: "SOL",
22
+ quote: "USDC",
23
+ slab: "PERCOLAT_SOL_...",
24
+ program: "Perco1ator111111111111111111111111111111111"
25
+ },
26
+ oracle: {
27
+ priceUsd: 181.61,
28
+ ageSecs: 2
29
+ },
30
+ engine: {
31
+ currentSlot: 346892118,
32
+ lastMarketSlot: 346892090,
33
+ fundingRateBpsPerHour: 0.82,
34
+ openInterestUsd: 2430000,
35
+ longOpenInterestUsd: 1320000,
36
+ shortOpenInterestUsd: 1110000,
37
+ insuranceUsd: 148000,
38
+ stressConsumedBps: 118,
39
+ stressLimitBps: 500
40
+ },
41
+ execution: {
42
+ bestBid: 181.52,
43
+ bestAsk: 181.71,
44
+ receipts: [
45
+ {
46
+ label: "latest fill",
47
+ sourceTimestamp: "2026-06-20T13:24:12Z",
48
+ spreadBps: 10.5,
49
+ impactBps: 8.4,
50
+ markout1mBps: 4.2,
51
+ markout5mBps: -1.7,
52
+ routeLatencyMs: 132,
53
+ priorityFeeMicrolamports: 2200
54
+ }
55
+ ]
56
+ },
57
+ account: {
58
+ side: "long",
59
+ positionSize: 420,
60
+ positionNotionalUsd: 76276.2,
61
+ collateralUsd: 8400,
62
+ unrealizedPnlUsd: 3067.2,
63
+ liquidationPrice: 162.94
64
+ },
65
+ history: {
66
+ fundingSkew: [
67
+ {
68
+ sourceTimestamp: "2026-06-20T13:24:00Z",
69
+ slot: 346892086,
70
+ fundingBpsPerHour: 0.82,
71
+ longOpenInterestUsd: 1320000,
72
+ shortOpenInterestUsd: 1110000,
73
+ stressConsumedBps: 118,
74
+ stressLimitBps: 500,
75
+ oracleAgeSec: 2.1
76
+ }
77
+ ]
78
+ }
79
+ };
16
80
 
17
81
  function usage() {
18
82
  return [
19
83
  "Usage:",
84
+ " perpscope init [output.json] [--force]",
20
85
  " perpscope compat report <capture.json>",
21
86
  " perpscope compat diff <previous.json> <current.json>",
22
- " perpscope compat doctor <capture.json>",
87
+ " perpscope compat doctor <capture.json> [--strict|--json]",
23
88
  " perpscope compat badge <capture.json> [--json|--markdown]",
24
89
  "",
25
90
  "Read-only only: the adapter rejects wallet, signer, transaction, instruction, order, private key, seed, mnemonic, and API key fields."
26
91
  ].join("\n");
27
92
  }
28
93
 
94
+ function initMessage(path) {
95
+ return [
96
+ `Created ${path}`,
97
+ "",
98
+ "Next:",
99
+ ` perpscope compat doctor ${path}`,
100
+ ` perpscope compat badge ${path}`,
101
+ "",
102
+ "Edit the capture with sanitized read-only decoded state before sharing it."
103
+ ].join("\n");
104
+ }
105
+
106
+ function initCapture(path = "perpscope.capture.json", options = {}) {
107
+ if (existsSync(path) && !options.force) {
108
+ throw new Error(`${path} already exists. Use --force to overwrite.`);
109
+ }
110
+ writeFileSync(path, `${JSON.stringify(CAPTURE_TEMPLATE, null, 2)}\n`);
111
+ return path;
112
+ }
113
+
29
114
  function readCapture(path) {
30
115
  if (!path) throw new Error("Missing capture path.");
31
116
  return parsePercolatorJson(readFileSync(path, "utf8"));
@@ -56,12 +141,28 @@ function formatDoctor(doctor) {
56
141
  return lines.join("\n");
57
142
  }
58
143
 
144
+ function doctorExitCode(doctor, options = {}) {
145
+ if (doctor.status === "rejected" || doctor.required.mapped < doctor.required.total) return 1;
146
+ if (options.strict && (
147
+ doctor.useful.mapped < doctor.useful.total ||
148
+ doctor.unknownFields.length ||
149
+ doctor.aliasSuggestions.length
150
+ )) return 2;
151
+ return 0;
152
+ }
153
+
59
154
  function main() {
60
155
  const [scope, command, ...rest] = args;
61
156
  if (!scope || scope === "--help" || scope === "-h") {
62
157
  console.log(usage());
63
158
  return;
64
159
  }
160
+ if (scope === "init") {
161
+ const output = command && !command.startsWith("--") ? command : "perpscope.capture.json";
162
+ const flags = [command, ...rest].filter(Boolean);
163
+ console.log(initMessage(initCapture(output, { force: flags.includes("--force") })));
164
+ return;
165
+ }
65
166
  if (scope !== "compat") throw new Error(`Unknown scope: ${scope}`);
66
167
  if (command === "report") {
67
168
  const input = readCapture(rest[0]);
@@ -76,7 +177,9 @@ function main() {
76
177
  }
77
178
  if (command === "doctor") {
78
179
  const input = readCapture(rest[0]);
79
- console.log(formatDoctor(buildCompatibilityDoctor(buildReport(input), { input })));
180
+ const doctor = buildCompatibilityDoctor(buildReport(input), { input });
181
+ console.log(rest.includes("--json") ? JSON.stringify(doctor, null, 2) : formatDoctor(doctor));
182
+ process.exitCode = doctorExitCode(doctor, { strict: rest.includes("--strict") });
80
183
  return;
81
184
  }
82
185
  if (command === "badge") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@perpscope/percolator-adapter",
3
- "version": "0.9.0",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "description": "Read-only Percolator adapter helpers for Solana perps terminals.",
6
6
  "main": "./index.js",
@@ -1,6 +1,6 @@
1
1
  import { normalizeFundingSkewHistory } from "./funding-history.js";
2
2
 
3
- export const PERPSCOPE_ADAPTER_VERSION = "0.9.0";
3
+ export const PERPSCOPE_ADAPTER_VERSION = "1.0.0";
4
4
 
5
5
  const KEYPAIR_FIELD_PATTERN = /(^|_)(secret|private|keypair|mnemonic|seed|walletPath|wallet)(_|$)/i;
6
6
  const HISTORY_COMMAND_KEYS = new Set([