@apifuse/provider-sdk 2.1.0-beta.5 → 2.1.0-beta.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 (163) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +2 -2
  3. package/SUBMISSION.md +2 -1
  4. package/bin/apifuse-check.ts +60 -6
  5. package/bin/apifuse-dev.ts +48 -5
  6. package/bin/apifuse-perf.ts +50 -11
  7. package/bin/apifuse-record.ts +35 -11
  8. package/bin/apifuse-submit-check.ts +1425 -3
  9. package/dist/ceremonies/index.d.ts +41 -0
  10. package/dist/ceremonies/index.js +490 -0
  11. package/dist/choice-token.d.ts +24 -0
  12. package/dist/choice-token.js +74 -0
  13. package/dist/cli/commands.d.ts +10 -0
  14. package/dist/cli/commands.js +80 -0
  15. package/dist/cli/create.d.ts +47 -0
  16. package/dist/cli/create.js +762 -0
  17. package/dist/config/loader.d.ts +107 -0
  18. package/dist/config/loader.js +935 -0
  19. package/dist/contract-json.d.ts +9 -0
  20. package/dist/contract-json.js +51 -0
  21. package/dist/contract-serialization.d.ts +4 -0
  22. package/dist/contract-serialization.js +78 -0
  23. package/dist/contract-types.d.ts +49 -0
  24. package/dist/contract-types.js +1 -0
  25. package/dist/contract.d.ts +6 -0
  26. package/dist/contract.js +155 -0
  27. package/dist/define.d.ts +97 -0
  28. package/dist/define.js +1320 -0
  29. package/dist/dev.d.ts +9 -0
  30. package/dist/dev.js +15 -0
  31. package/dist/errors.d.ts +59 -0
  32. package/dist/errors.js +97 -0
  33. package/dist/i18n/catalog.d.ts +29 -0
  34. package/dist/i18n/catalog.js +159 -0
  35. package/dist/i18n/index.d.ts +2 -0
  36. package/dist/i18n/index.js +2 -0
  37. package/dist/i18n/keys.d.ts +10 -0
  38. package/dist/i18n/keys.js +34 -0
  39. package/dist/index.d.ts +41 -0
  40. package/dist/index.js +37 -0
  41. package/dist/lint.d.ts +73 -0
  42. package/dist/lint.js +702 -0
  43. package/dist/observability.d.ts +5 -0
  44. package/dist/observability.js +39 -0
  45. package/dist/provider.d.ts +9 -0
  46. package/dist/provider.js +8 -0
  47. package/dist/public-schema-field-lint.d.ts +2 -0
  48. package/dist/public-schema-field-lint.js +158 -0
  49. package/dist/recipes/gov-api.d.ts +19 -0
  50. package/dist/recipes/gov-api.js +72 -0
  51. package/dist/recipes/rest-api.d.ts +21 -0
  52. package/dist/recipes/rest-api.js +115 -0
  53. package/dist/runtime/auth-flow.d.ts +14 -0
  54. package/dist/runtime/auth-flow.js +44 -0
  55. package/dist/runtime/browser.d.ts +25 -0
  56. package/dist/runtime/browser.js +1034 -0
  57. package/dist/runtime/cache.d.ts +10 -0
  58. package/dist/runtime/cache.js +372 -0
  59. package/dist/runtime/choice.d.ts +15 -0
  60. package/dist/runtime/choice.js +435 -0
  61. package/dist/runtime/credential.d.ts +8 -0
  62. package/dist/runtime/credential.js +61 -0
  63. package/dist/runtime/env.d.ts +2 -0
  64. package/dist/runtime/env.js +10 -0
  65. package/dist/runtime/executor.d.ts +16 -0
  66. package/dist/runtime/executor.js +51 -0
  67. package/dist/runtime/http.d.ts +8 -0
  68. package/dist/runtime/http.js +706 -0
  69. package/dist/runtime/insights.d.ts +9 -0
  70. package/dist/runtime/insights.js +324 -0
  71. package/dist/runtime/instrumentation.d.ts +8 -0
  72. package/dist/runtime/instrumentation.js +269 -0
  73. package/dist/runtime/key-derivation.d.ts +24 -0
  74. package/dist/runtime/key-derivation.js +73 -0
  75. package/dist/runtime/keyring.d.ts +25 -0
  76. package/dist/runtime/keyring.js +93 -0
  77. package/dist/runtime/namespace.d.ts +9 -0
  78. package/dist/runtime/namespace.js +19 -0
  79. package/dist/runtime/otlp.d.ts +39 -0
  80. package/dist/runtime/otlp.js +103 -0
  81. package/dist/runtime/perf.d.ts +12 -0
  82. package/dist/runtime/perf.js +52 -0
  83. package/dist/runtime/prevalidate.d.ts +12 -0
  84. package/dist/runtime/prevalidate.js +173 -0
  85. package/dist/runtime/provider.d.ts +2 -0
  86. package/dist/runtime/provider.js +11 -0
  87. package/dist/runtime/proxy-errors.d.ts +21 -0
  88. package/dist/runtime/proxy-errors.js +83 -0
  89. package/dist/runtime/proxy-telemetry.d.ts +8 -0
  90. package/dist/runtime/proxy-telemetry.js +174 -0
  91. package/dist/runtime/redis.d.ts +17 -0
  92. package/dist/runtime/redis.js +82 -0
  93. package/dist/runtime/request-options.d.ts +3 -0
  94. package/dist/runtime/request-options.js +42 -0
  95. package/dist/runtime/state.d.ts +17 -0
  96. package/dist/runtime/state.js +344 -0
  97. package/dist/runtime/stealth.d.ts +18 -0
  98. package/dist/runtime/stealth.js +827 -0
  99. package/dist/runtime/stt.d.ts +22 -0
  100. package/dist/runtime/stt.js +480 -0
  101. package/dist/runtime/trace.d.ts +26 -0
  102. package/dist/runtime/trace.js +142 -0
  103. package/dist/runtime/waterfall.d.ts +12 -0
  104. package/dist/runtime/waterfall.js +147 -0
  105. package/dist/schema.d.ts +74 -0
  106. package/dist/schema.js +243 -0
  107. package/dist/serve.d.ts +1 -0
  108. package/dist/serve.js +1 -0
  109. package/dist/server/index.d.ts +3 -0
  110. package/dist/server/index.js +2 -0
  111. package/dist/server/serve.d.ts +64 -0
  112. package/dist/server/serve.js +1110 -0
  113. package/dist/server/types.d.ts +136 -0
  114. package/dist/server/types.js +86 -0
  115. package/dist/stealth/profiles.d.ts +4 -0
  116. package/dist/stealth/profiles.js +259 -0
  117. package/dist/stream.d.ts +44 -0
  118. package/dist/stream.js +151 -0
  119. package/dist/testing/helpers.d.ts +23 -0
  120. package/dist/testing/helpers.js +95 -0
  121. package/dist/testing/index.d.ts +2 -0
  122. package/dist/testing/index.js +2 -0
  123. package/dist/testing/run.d.ts +34 -0
  124. package/dist/testing/run.js +303 -0
  125. package/dist/types.d.ts +1324 -0
  126. package/dist/types.js +61 -0
  127. package/dist/utils/date.d.ts +6 -0
  128. package/dist/utils/date.js +101 -0
  129. package/dist/utils/parse.d.ts +16 -0
  130. package/dist/utils/parse.js +51 -0
  131. package/dist/utils/text.d.ts +4 -0
  132. package/dist/utils/text.js +14 -0
  133. package/dist/utils/transform.d.ts +8 -0
  134. package/dist/utils/transform.js +48 -0
  135. package/package.json +42 -25
  136. package/src/ceremonies/index.ts +8 -2
  137. package/src/choice-token.ts +1 -0
  138. package/src/cli/commands.ts +8 -5
  139. package/src/cli/create.ts +28 -0
  140. package/src/cli/templates/provider/operations/ping.ts.tpl +3 -2
  141. package/src/cli/templates/provider/schemas/ping.ts.tpl +8 -0
  142. package/src/config/loader.ts +19 -1
  143. package/src/contract-json.ts +75 -0
  144. package/src/contract-serialization.ts +89 -0
  145. package/src/contract-types.ts +52 -0
  146. package/src/contract.ts +215 -0
  147. package/src/define.ts +37 -2
  148. package/src/errors.ts +15 -0
  149. package/src/i18n/catalog.ts +156 -0
  150. package/src/index.ts +22 -1
  151. package/src/lint.ts +256 -37
  152. package/src/provider.ts +45 -2
  153. package/src/runtime/browser.ts +685 -30
  154. package/src/runtime/cache.ts +35 -89
  155. package/src/runtime/choice.ts +760 -0
  156. package/src/runtime/executor.ts +19 -2
  157. package/src/runtime/redis.ts +116 -0
  158. package/src/runtime/state.ts +487 -0
  159. package/src/runtime/stealth.ts +8 -1
  160. package/src/server/serve.ts +361 -46
  161. package/src/server/types.ts +2 -0
  162. package/src/testing/run.ts +16 -3
  163. package/src/types.ts +209 -6
package/dist/types.js ADDED
@@ -0,0 +1,61 @@
1
+ export const OPERATION_TIMEOUT_MS_MIN = 1;
2
+ export const OPERATION_TIMEOUT_MS_MAX = 60_000;
3
+ export const STREAM_HEARTBEAT_MS_MIN = 1_000;
4
+ export const STREAM_HEARTBEAT_MS_MAX = 60_000;
5
+ export const STREAM_IDLE_TIMEOUT_MS_MIN = 1_000;
6
+ export const STREAM_IDLE_TIMEOUT_MS_MAX = 300_000;
7
+ export const STREAM_MAX_DURATION_MS_MIN = 1_000;
8
+ export const STREAM_MAX_DURATION_MS_MAX = 1_800_000;
9
+ export const STREAM_CHUNK_BYTES_MIN = 1;
10
+ export const STREAM_CHUNK_BYTES_MAX = 1_048_576;
11
+ export const DEFAULT_OPERATION_TRANSPORT = {
12
+ kind: "json",
13
+ };
14
+ /**
15
+ * Common probe interval examples retained for discoverability/backwards
16
+ * compatibility. This list is not exhaustive; any positive `ms`-style duration
17
+ * string accepted by `@types/ms` (for example `2m`, `8h`, or `1 day`) is valid.
18
+ */
19
+ export const PROBE_INTERVALS = [
20
+ "30s",
21
+ "1m",
22
+ "3m",
23
+ "5m",
24
+ "15m",
25
+ "30m",
26
+ "1h",
27
+ "2h",
28
+ "8h",
29
+ "24h",
30
+ ];
31
+ export const HEALTH_CHECK_TIMEOUT_MS_MIN = 1;
32
+ export const HEALTH_CHECK_TIMEOUT_MS_MAX = 60_000;
33
+ export const HEALTH_CHECK_DEGRADED_THRESHOLD_MS_MIN = 1;
34
+ export const HEALTH_CHECK_DEGRADED_THRESHOLD_MS_MAX = 60_000;
35
+ export const HttpRetryPreset = {
36
+ Off: "off",
37
+ TransportTransient: "transport_transient",
38
+ SafeRead: "safe_read",
39
+ AggressiveRead: "aggressive_read",
40
+ RateLimitAware: "rate_limit_aware",
41
+ };
42
+ export const HttpRetryJitter = {
43
+ None: "none",
44
+ Full: "full",
45
+ Equal: "equal",
46
+ };
47
+ export const HttpRetryDelayStrategy = {
48
+ Fixed: "fixed",
49
+ Exponential: "exponential",
50
+ };
51
+ export const HttpRetryAfterPolicy = {
52
+ Ignore: "ignore",
53
+ /** Honor Retry-After up to maxDelayMs. */
54
+ Respect: "respect",
55
+ /** Honor Retry-After but also cap it to the SDK-computed backoff delay. */
56
+ Cap: "cap",
57
+ };
58
+ export const HttpRetryUnsafeMethodPolicy = {
59
+ Reject: "reject",
60
+ AllowExplicitUnsafe: "allow_explicit_unsafe",
61
+ };
@@ -0,0 +1,6 @@
1
+ /** Convert various date formats to ISO 8601 string */
2
+ export declare function toISODate(v: unknown, timezone: string): string;
3
+ /** Get today's date as ISO 8601 string in given timezone */
4
+ export declare function today(timezone: string): string;
5
+ /** Get current hour as string in given timezone */
6
+ export declare function currentHour(timezone: string): string;
@@ -0,0 +1,101 @@
1
+ function getDateParts(date, timezone) {
2
+ const formatter = new Intl.DateTimeFormat("en-CA", {
3
+ timeZone: timezone,
4
+ hour12: false,
5
+ year: "numeric",
6
+ month: "2-digit",
7
+ day: "2-digit",
8
+ hour: "2-digit",
9
+ minute: "2-digit",
10
+ second: "2-digit",
11
+ });
12
+ const parts = Object.fromEntries(formatter.formatToParts(date).map((part) => [part.type, part.value]));
13
+ return {
14
+ year: parts.year,
15
+ month: parts.month,
16
+ day: parts.day,
17
+ hour: parts.hour,
18
+ minute: parts.minute,
19
+ second: parts.second,
20
+ };
21
+ }
22
+ function getTimezoneOffsetMinutes(date, timezone) {
23
+ const parts = getDateParts(date, timezone);
24
+ const asUTC = Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour), Number(parts.minute), Number(parts.second));
25
+ return (asUTC - date.getTime()) / 60000;
26
+ }
27
+ function buildUtcInstantFromLocalParts(year, month, day, hour, minute, second, timezone) {
28
+ const guess = Date.UTC(year, month - 1, day, hour, minute, second);
29
+ let utc = guess;
30
+ for (let i = 0; i < 2; i += 1) {
31
+ const offset = getTimezoneOffsetMinutes(new Date(utc), timezone);
32
+ const nextUtc = guess - offset * 60_000;
33
+ if (nextUtc === utc) {
34
+ break;
35
+ }
36
+ utc = nextUtc;
37
+ }
38
+ return new Date(utc);
39
+ }
40
+ function formatOffset(offsetMinutes) {
41
+ const sign = offsetMinutes >= 0 ? "+" : "-";
42
+ const abs = Math.abs(offsetMinutes);
43
+ const hours = String(Math.floor(abs / 60)).padStart(2, "0");
44
+ const minutes = String(abs % 60).padStart(2, "0");
45
+ return `${sign}${hours}:${minutes}`;
46
+ }
47
+ function formatIso(date, timezone) {
48
+ const parts = getDateParts(date, timezone);
49
+ const offset = getTimezoneOffsetMinutes(date, timezone);
50
+ return `${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:${parts.second}${formatOffset(offset)}`;
51
+ }
52
+ function parseDateValue(v, timezone) {
53
+ if (v instanceof Date) {
54
+ return Number.isNaN(v.getTime()) ? null : v;
55
+ }
56
+ if (typeof v === "number") {
57
+ const date = new Date(v);
58
+ return Number.isNaN(date.getTime()) ? null : date;
59
+ }
60
+ if (typeof v !== "string") {
61
+ return null;
62
+ }
63
+ const value = v.trim();
64
+ if (value === "") {
65
+ return null;
66
+ }
67
+ const compactDate = /^(\d{4})(\d{2})(\d{2})$/;
68
+ const compactDateTime = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})?$/;
69
+ const dashedDate = /^(\d{4})-(\d{2})-(\d{2})$/;
70
+ let match = value.match(compactDateTime);
71
+ if (match) {
72
+ const [, year, month, day, hour, minute, second = "00"] = match;
73
+ return buildUtcInstantFromLocalParts(Number(year), Number(month), Number(day), Number(hour), Number(minute), Number(second), timezone);
74
+ }
75
+ match = value.match(compactDate);
76
+ if (match) {
77
+ const [, year, month, day] = match;
78
+ return buildUtcInstantFromLocalParts(Number(year), Number(month), Number(day), 0, 0, 0, timezone);
79
+ }
80
+ match = value.match(dashedDate);
81
+ if (match) {
82
+ const [, year, month, day] = match;
83
+ return buildUtcInstantFromLocalParts(Number(year), Number(month), Number(day), 0, 0, 0, timezone);
84
+ }
85
+ const parsed = new Date(value);
86
+ return Number.isNaN(parsed.getTime()) ? null : parsed;
87
+ }
88
+ /** Convert various date formats to ISO 8601 string */
89
+ export function toISODate(v, timezone) {
90
+ const date = parseDateValue(v, timezone);
91
+ return date ? formatIso(date, timezone) : "";
92
+ }
93
+ /** Get today's date as ISO 8601 string in given timezone */
94
+ export function today(timezone) {
95
+ const parts = getDateParts(new Date(), timezone);
96
+ return `${parts.year}-${parts.month}-${parts.day}`;
97
+ }
98
+ /** Get current hour as string in given timezone */
99
+ export function currentHour(timezone) {
100
+ return getDateParts(new Date(), timezone).hour;
101
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Unwrap nested envelope by dot-path
3
+ * e.g., unwrapEnvelope({response: {body: {items: [...]}}}, 'response.body.items')
4
+ */
5
+ export declare function unwrapEnvelope(data: unknown, path?: string): unknown;
6
+ /**
7
+ * Pivot array by field — generalized version of pivotByCategory
8
+ * e.g., pivotByField([{type:'A', val:1},{type:'B', val:2}], 'type', 'val')
9
+ * → { A: 1, B: 2 }
10
+ */
11
+ export declare function pivotByField(items: unknown[], keyField: string, valueField: string): Record<string, unknown>;
12
+ /**
13
+ * Parse XML items from string — returns array
14
+ * Uses simple regex (no DOM), suitable for structured XML
15
+ */
16
+ export declare function parseXmlItems(xml: string, tag: string): string[];
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Unwrap nested envelope by dot-path
3
+ * e.g., unwrapEnvelope({response: {body: {items: [...]}}}, 'response.body.items')
4
+ */
5
+ export function unwrapEnvelope(data, path) {
6
+ if (!path || path.trim() === "") {
7
+ return data;
8
+ }
9
+ const segments = path.split(".").filter(Boolean);
10
+ let current = data;
11
+ for (const segment of segments) {
12
+ if (!current || typeof current !== "object") {
13
+ return undefined;
14
+ }
15
+ current = current[segment];
16
+ }
17
+ return current;
18
+ }
19
+ /**
20
+ * Pivot array by field — generalized version of pivotByCategory
21
+ * e.g., pivotByField([{type:'A', val:1},{type:'B', val:2}], 'type', 'val')
22
+ * → { A: 1, B: 2 }
23
+ */
24
+ export function pivotByField(items, keyField, valueField) {
25
+ const result = {};
26
+ for (const item of items) {
27
+ if (!item || typeof item !== "object") {
28
+ continue;
29
+ }
30
+ const record = item;
31
+ const key = record[keyField];
32
+ const value = record[valueField];
33
+ if (key === undefined || key === null || value === undefined) {
34
+ continue;
35
+ }
36
+ result[String(key)] = value;
37
+ }
38
+ return result;
39
+ }
40
+ /**
41
+ * Parse XML items from string — returns array
42
+ * Uses simple regex (no DOM), suitable for structured XML
43
+ */
44
+ export function parseXmlItems(xml, tag) {
45
+ const matches = [
46
+ ...xml.matchAll(new RegExp(`<${tag}[^>]*>([\\s\\S]*?)</${tag}>`, "gi")),
47
+ ];
48
+ return matches
49
+ .map((match) => match[1].replace(/<[^>]+>/g, "").trim())
50
+ .filter((item) => item.length > 0);
51
+ }
@@ -0,0 +1,4 @@
1
+ /** Remove HTML tags from string */
2
+ export declare function stripHtml(html: string): string;
3
+ /** Truncate string to maxLength with optional suffix */
4
+ export declare function truncate(str: string, maxLength: number, suffix?: string): string;
@@ -0,0 +1,14 @@
1
+ /** Remove HTML tags from string */
2
+ export function stripHtml(html) {
3
+ return html
4
+ .replace(/<[^>]*>/g, "")
5
+ .replace(/\s+/g, " ")
6
+ .trim();
7
+ }
8
+ /** Truncate string to maxLength with optional suffix */
9
+ export function truncate(str, maxLength, suffix = "...") {
10
+ if (str.length <= maxLength) {
11
+ return str;
12
+ }
13
+ return `${str.slice(0, maxLength)}${suffix}`;
14
+ }
@@ -0,0 +1,8 @@
1
+ /** Parse any value to number (returns 0 if not parseable) */
2
+ export declare function toNumber(v: unknown): number;
3
+ /** Parse any value to float */
4
+ export declare function toFloat(v: unknown, decimals?: number): number;
5
+ /** Parse any value to integer */
6
+ export declare function toInt(v: unknown): number;
7
+ /** Parse any value to boolean (handles "true", "1", "yes", true, 1) */
8
+ export declare function toBoolean(v: unknown): boolean;
@@ -0,0 +1,48 @@
1
+ /** Parse any value to number (returns 0 if not parseable) */
2
+ export function toNumber(v) {
3
+ if (typeof v === "number") {
4
+ return Number.isFinite(v) ? v : 0;
5
+ }
6
+ if (typeof v === "boolean") {
7
+ return v ? 1 : 0;
8
+ }
9
+ if (typeof v === "string") {
10
+ const cleaned = v.replaceAll(",", "").trim();
11
+ if (cleaned === "") {
12
+ return 0;
13
+ }
14
+ const parsed = Number(cleaned);
15
+ return Number.isFinite(parsed) ? parsed : 0;
16
+ }
17
+ return 0;
18
+ }
19
+ /** Parse any value to float */
20
+ export function toFloat(v, decimals) {
21
+ const parsed = toNumber(v);
22
+ if (!Number.isFinite(parsed)) {
23
+ return 0;
24
+ }
25
+ if (decimals === undefined) {
26
+ return parsed;
27
+ }
28
+ const factor = 10 ** decimals;
29
+ return Math.round(parsed * factor) / factor;
30
+ }
31
+ /** Parse any value to integer */
32
+ export function toInt(v) {
33
+ return Math.round(toNumber(v));
34
+ }
35
+ /** Parse any value to boolean (handles "true", "1", "yes", true, 1) */
36
+ export function toBoolean(v) {
37
+ if (typeof v === "boolean") {
38
+ return v;
39
+ }
40
+ if (typeof v === "number") {
41
+ return v === 1;
42
+ }
43
+ if (typeof v === "string") {
44
+ const normalized = v.trim().toLowerCase();
45
+ return ["true", "1", "yes", "y"].includes(normalized);
46
+ }
47
+ return false;
48
+ }
package/package.json CHANGED
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "name": "@apifuse/provider-sdk",
3
- "version": "2.1.0-beta.5",
3
+ "version": "2.1.0-beta.8",
4
4
  "private": false,
5
5
  "type": "module",
6
- "description": "APIFuse Provider SDK Build providers with zero architectural constraints",
6
+ "description": "APIFuse Provider SDK \u2014 Build providers with zero architectural constraints",
7
7
  "license": "MIT",
8
- "main": "./src/index.ts",
9
- "types": "./src/index.ts",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
10
  "publishConfig": {
11
11
  "access": "public"
12
12
  },
13
13
  "files": [
14
+ "dist",
14
15
  "src",
15
16
  "!src/__tests__",
16
17
  "!src/__tests__/**",
@@ -35,46 +36,54 @@
35
36
  },
36
37
  "exports": {
37
38
  ".": {
38
- "default": "./src/index.ts",
39
- "import": "./src/index.ts",
40
- "types": "./src/index.ts"
39
+ "types": "./dist/index.d.ts",
40
+ "import": "./dist/index.js",
41
+ "default": "./dist/index.js"
41
42
  },
42
43
  "./provider": {
43
- "default": "./src/provider.ts",
44
- "import": "./src/provider.ts",
45
- "types": "./src/provider.ts"
44
+ "types": "./dist/provider.d.ts",
45
+ "import": "./dist/provider.js",
46
+ "default": "./dist/provider.js"
47
+ },
48
+ "./contract": {
49
+ "types": "./dist/contract.d.ts",
50
+ "import": "./dist/contract.js",
51
+ "default": "./dist/contract.js"
46
52
  },
47
53
  "./server": {
48
- "default": "./src/server/index.ts",
49
- "import": "./src/server/index.ts",
50
- "types": "./src/server/index.ts"
54
+ "types": "./dist/server/index.d.ts",
55
+ "import": "./dist/server/index.js",
56
+ "default": "./dist/server/index.js"
51
57
  },
52
58
  "./testing": {
53
- "default": "./src/testing/index.ts",
54
- "import": "./src/testing/index.ts",
55
- "types": "./src/testing/index.ts"
59
+ "types": "./dist/testing/index.d.ts",
60
+ "import": "./dist/testing/index.js",
61
+ "default": "./dist/testing/index.js"
56
62
  },
57
63
  "./create": {
58
- "default": "./src/cli/create.ts",
59
- "import": "./src/cli/create.ts",
60
- "types": "./src/cli/create.ts"
64
+ "types": "./dist/cli/create.d.ts",
65
+ "import": "./dist/cli/create.js",
66
+ "default": "./dist/cli/create.js"
61
67
  }
62
68
  },
63
69
  "scripts": {
64
- "lint": "biome check",
70
+ "lint": "biome lint .",
65
71
  "lint:fix": "biome lint --write",
66
72
  "format": "biome format --write",
67
73
  "type-check": "tsgo --noEmit",
68
74
  "test": "bun test",
69
- "check": "bun run lint && bun run type-check",
70
- "pack:check": "bun bin/apifuse-pack-check.ts",
71
- "pack:smoke": "bun bin/apifuse-pack-smoke.ts"
75
+ "check": "bun run lint && bun run type-check && bun run build",
76
+ "pack:check": "bun run build && bun bin/apifuse-pack-check.ts",
77
+ "pack:smoke": "bun run build && bun bin/apifuse-pack-smoke.ts",
78
+ "release:guard": "bun scripts/guard-release-pr.ts",
79
+ "format:check": "biome format .",
80
+ "build": "tsgo -p tsconfig.build.json"
72
81
  },
73
82
  "devDependencies": {
74
83
  "@biomejs/biome": "^2.5.0",
75
84
  "@types/bun": "latest",
76
85
  "@types/node": "^25.9.3",
77
- "typescript": "^6.0.3"
86
+ "@typescript/native-preview": "7.0.0-dev.20260419.1"
78
87
  },
79
88
  "dependencies": {
80
89
  "@clack/prompts": "^1.5.1",
@@ -90,5 +99,13 @@
90
99
  "re2-wasm": "^1.0",
91
100
  "safe-regex": "^2.1",
92
101
  "zod": "^4.4.3"
93
- }
102
+ },
103
+ "repository": {
104
+ "type": "git",
105
+ "url": "git+https://github.com/APIFuseHQ/provider-sdk.git"
106
+ },
107
+ "bugs": {
108
+ "url": "https://github.com/APIFuseHQ/provider-sdk/issues"
109
+ },
110
+ "homepage": "https://github.com/APIFuseHQ/provider-sdk#readme"
94
111
  }
@@ -8,7 +8,12 @@ import {
8
8
  TurnValidationError,
9
9
  ValidationError,
10
10
  } from "../errors";
11
- import type { AuthFlowDefinition, AuthTurn, FlowContext } from "../types";
11
+ import type {
12
+ AuthFlowDefinition,
13
+ AuthFlowInputHandler,
14
+ AuthTurn,
15
+ FlowContext,
16
+ } from "../types";
12
17
 
13
18
  type TurnKind =
14
19
  | "abort"
@@ -21,7 +26,7 @@ type TurnKind =
21
26
  | "redirect"
22
27
  | "retry";
23
28
 
24
- type CeremonyHandler = AuthFlowDefinition["start"];
29
+ type CeremonyHandler = AuthFlowInputHandler;
25
30
 
26
31
  type JsonObject = Record<string, unknown>;
27
32
 
@@ -44,6 +49,7 @@ const authTurnSchema = {
44
49
  additionalProperties: true,
45
50
  },
46
51
  hint: { type: "string" },
52
+ hintKey: { type: "string" },
47
53
  timing: {
48
54
  type: "object",
49
55
  additionalProperties: false,
@@ -13,6 +13,7 @@ export type ProviderChoiceTokenErrorReason =
13
13
  | "invalid_shape"
14
14
  | "invalid_signature"
15
15
  | "invalid_payload"
16
+ | "invalid_binding"
16
17
  | "stale";
17
18
 
18
19
  export class ProviderChoiceTokenError extends Error {
@@ -36,14 +36,14 @@ export const COMMAND_MANIFEST: Record<
36
36
  summary:
37
37
  "Start the local provider dev server with the standard provider server contract.",
38
38
  usage: "apifuse dev [path]",
39
- examples: ["apifuse dev .", "apifuse dev providers/airkorea"],
39
+ examples: ["apifuse dev .", "apifuse dev providers/korea-air-quality"],
40
40
  modulePath: "./apifuse-dev",
41
41
  },
42
42
  check: {
43
43
  name: "check",
44
44
  summary: "Validate provider structure, metadata, fixtures, and schemas.",
45
45
  usage: "apifuse check [path]",
46
- examples: ["apifuse check .", "apifuse check providers/airkorea"],
46
+ examples: ["apifuse check .", "apifuse check providers/korea-air-quality"],
47
47
  modulePath: "./apifuse-check",
48
48
  },
49
49
  "submit-check": {
@@ -72,7 +72,7 @@ export const COMMAND_MANIFEST: Record<
72
72
  usage:
73
73
  'apifuse record [path] --operation <operation> --params \'{"value":"hello"}\'',
74
74
  examples: [
75
- 'apifuse record providers/airkorea --operation realtime --params \'{"stationName":"종로구"}\'',
75
+ 'apifuse record providers/korea-air-quality --operation realtime --params \'{"stationName":"종로구"}\'',
76
76
  ],
77
77
  modulePath: "./apifuse-record",
78
78
  },
@@ -80,7 +80,10 @@ export const COMMAND_MANIFEST: Record<
80
80
  name: "test",
81
81
  summary: "Run provider-focused tests and surface actionable failures.",
82
82
  usage: "apifuse test [path] [--json] [--verbose]",
83
- examples: ["apifuse test .", "apifuse test providers/airkorea --json"],
83
+ examples: [
84
+ "apifuse test .",
85
+ "apifuse test providers/korea-air-quality --json",
86
+ ],
84
87
  modulePath: "./apifuse-test",
85
88
  },
86
89
  perf: {
@@ -90,7 +93,7 @@ export const COMMAND_MANIFEST: Record<
90
93
  usage:
91
94
  "apifuse perf <path> --operation <operation> [--params '<json>'] [options]",
92
95
  examples: [
93
- 'apifuse perf providers/airkorea --operation realtime --params \'{"stationName":"종로구"}\' --runs 5',
96
+ 'apifuse perf providers/korea-air-quality --operation realtime --params \'{"stationName":"종로구"}\' --runs 5',
94
97
  ],
95
98
  modulePath: "./apifuse-perf",
96
99
  },
package/src/cli/create.ts CHANGED
@@ -502,6 +502,15 @@ export async function buildProviderCreatePlan(
502
502
  path: resolve(providerRoot, "operations", "ping.ts"),
503
503
  content: await renderTemplate("operations/ping.ts.tpl", {
504
504
  DISPLAY_NAME: escapeTemplate(options.displayName),
505
+ HANDLER_CTX: options.runtime === "browser" ? "ctx" : "_ctx",
506
+ BROWSER_HANDLER_BLOCK:
507
+ options.runtime === "browser"
508
+ ? '\n const page = await ctx.browser.newPage();\n await page.goto("https://example.com");\n const title = await page.title();\n const frames = await page.frames();\n await page.close();\n'
509
+ : "",
510
+ BROWSER_RESPONSE_FIELDS:
511
+ options.runtime === "browser"
512
+ ? ",\n pageTitle: title,\n frameCount: frames.length"
513
+ : "",
505
514
  }),
506
515
  },
507
516
  {
@@ -701,6 +710,14 @@ function renderStarterLocaleCatalog(
701
710
  locale === "ko"
702
711
  ? "생성된 provider가 샘플 payload를 round-trip했음을 보여주는 사람이 읽을 수 있는 확인 메시지"
703
712
  : "Human-readable confirmation that the generated provider round-tripped the sample payload.",
713
+ pageTitle:
714
+ locale === "ko"
715
+ ? "browser 런타임 provider일 때 로드된 페이지의 제목 (해당되지 않으면 생략)"
716
+ : "Title of the loaded page when the provider uses the browser runtime; omitted otherwise.",
717
+ frameCount:
718
+ locale === "ko"
719
+ ? "browser 런타임 provider일 때 로드된 페이지의 frame 개수 (해당되지 않으면 생략)"
720
+ : "Number of frames in the loaded page when the provider uses the browser runtime; omitted otherwise.",
704
721
  },
705
722
  },
706
723
  };
@@ -744,6 +761,17 @@ function renderAuthBlock(authMode: CreateAuthMode): string {
744
761
  },
745
762
  hint: "Generated placeholder credential flow completed. Replace this with real auth logic.",
746
763
  }),
764
+ refresh: async () => ({
765
+ kind: "complete",
766
+ turnId: crypto.randomUUID(),
767
+ data: {
768
+ credential: {
769
+ username: "replace-with-refreshed-username",
770
+ password: "replace-with-refreshed-password",
771
+ },
772
+ },
773
+ hint: "Return refreshed credential data here, or throw AuthError with code AUTH_REQUIRED when silent refresh is not possible.",
774
+ }),
747
775
  },
748
776
  }`;
749
777
  case "oauth2":
@@ -6,10 +6,11 @@ export const pingOperation = defineOperation({
6
6
  descriptionKey: "operations.ping.description",
7
7
  input: pingInputSchema,
8
8
  output: pingOutputSchema,
9
- handler: async (_ctx, input) => {
9
+ handler: async ({{HANDLER_CTX}}, input) => {
10
+ {{BROWSER_HANDLER_BLOCK}}
10
11
  return {
11
12
  ok: true,
12
- message: "{{DISPLAY_NAME}} received: " + input.value,
13
+ message: "{{DISPLAY_NAME}} received: " + input.value{{BROWSER_RESPONSE_FIELDS}},
13
14
  };
14
15
  },
15
16
  fixtures: {
@@ -11,6 +11,14 @@ export const pingOutputSchema = describeKey(
11
11
  z.object({
12
12
  ok: describeKey(z.boolean(), "schemaDescriptions.output.ok"),
13
13
  message: describeKey(z.string(), "schemaDescriptions.output.message"),
14
+ pageTitle: describeKey(
15
+ z.string().optional(),
16
+ "schemaDescriptions.output.pageTitle",
17
+ ),
18
+ frameCount: describeKey(
19
+ z.number().int().nonnegative().optional(),
20
+ "schemaDescriptions.output.frameCount",
21
+ ),
14
22
  }),
15
23
  "schemaDescriptions.output.root",
16
24
  );
@@ -16,6 +16,8 @@ export const DEFAULT_PROXY_LIFETIME_ENV =
16
16
  "APIFUSE__PROXY__DEFAULT_LIFETIME_MINUTES";
17
17
  export const PROVIDER_CACHE_REDIS_URL_ENV =
18
18
  "APIFUSE__PROVIDER__CACHE_REDIS_URL";
19
+ export const PROVIDER_STATE_REDIS_URL_ENV =
20
+ "APIFUSE__PROVIDER__STATE_REDIS_URL";
19
21
  export const REDIS_URL_ENV = "APIFUSE__REDIS__URL";
20
22
 
21
23
  export type ProxyOptions = {
@@ -175,6 +177,19 @@ const SMARTPROXY_EXTRACTION_SOFT_REFRESH_MS = 10_000;
175
177
 
176
178
  function redisUrlFromEnv(): string | undefined {
177
179
  return (
180
+ process.env.APIFUSE__PROVIDER__CACHE_REDIS_URL?.trim() ||
181
+ process.env[REDIS_URL_ENV]?.trim() ||
182
+ undefined
183
+ );
184
+ }
185
+
186
+ export function providerCacheRedisUrlFromEnv(): string | undefined {
187
+ return redisUrlFromEnv();
188
+ }
189
+
190
+ export function providerStateRedisUrlFromEnv(): string | undefined {
191
+ return (
192
+ process.env[PROVIDER_STATE_REDIS_URL_ENV]?.trim() ||
178
193
  process.env[PROVIDER_CACHE_REDIS_URL_ENV]?.trim() ||
179
194
  process.env[REDIS_URL_ENV]?.trim() ||
180
195
  undefined
@@ -620,7 +635,10 @@ function selectProxyPoolIndex(poolSize: number, attempt = 0): number {
620
635
  if (poolSize <= 1) {
621
636
  return 0;
622
637
  }
623
- return Math.max(0, Math.floor(attempt)) % poolSize;
638
+ const normalizedAttempt = Number.isFinite(attempt)
639
+ ? Math.max(0, Math.floor(attempt))
640
+ : 0;
641
+ return normalizedAttempt % poolSize;
624
642
  }
625
643
 
626
644
  function buildSmartproxyCacheKey(