@rcrsr/rill 0.8.0 → 0.8.2

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 (43) hide show
  1. package/README.md +28 -0
  2. package/dist/error-registry.d.ts.map +1 -1
  3. package/dist/error-registry.js +164 -0
  4. package/dist/error-registry.js.map +1 -1
  5. package/dist/ext/crypto/index.d.ts +32 -0
  6. package/dist/ext/crypto/index.d.ts.map +1 -0
  7. package/dist/ext/crypto/index.js +143 -0
  8. package/dist/ext/crypto/index.js.map +1 -0
  9. package/dist/ext/exec/index.d.ts +45 -0
  10. package/dist/ext/exec/index.d.ts.map +1 -0
  11. package/dist/ext/exec/index.js +168 -0
  12. package/dist/ext/exec/index.js.map +1 -0
  13. package/dist/ext/exec/runner.d.ts +62 -0
  14. package/dist/ext/exec/runner.d.ts.map +1 -0
  15. package/dist/ext/exec/runner.js +168 -0
  16. package/dist/ext/exec/runner.js.map +1 -0
  17. package/dist/ext/fetch/index.d.ts +68 -0
  18. package/dist/ext/fetch/index.d.ts.map +1 -0
  19. package/dist/ext/fetch/index.js +259 -0
  20. package/dist/ext/fetch/index.js.map +1 -0
  21. package/dist/ext/fetch/request.d.ts +90 -0
  22. package/dist/ext/fetch/request.d.ts.map +1 -0
  23. package/dist/ext/fetch/request.js +413 -0
  24. package/dist/ext/fetch/request.js.map +1 -0
  25. package/dist/ext/fs/index.d.ts +39 -0
  26. package/dist/ext/fs/index.d.ts.map +1 -0
  27. package/dist/ext/fs/index.js +560 -0
  28. package/dist/ext/fs/index.js.map +1 -0
  29. package/dist/ext/fs/sandbox.d.ts +78 -0
  30. package/dist/ext/fs/sandbox.d.ts.map +1 -0
  31. package/dist/ext/fs/sandbox.js +208 -0
  32. package/dist/ext/fs/sandbox.js.map +1 -0
  33. package/dist/ext/kv/index.d.ts +46 -0
  34. package/dist/ext/kv/index.d.ts.map +1 -0
  35. package/dist/ext/kv/index.js +215 -0
  36. package/dist/ext/kv/index.js.map +1 -0
  37. package/dist/ext/kv/store.d.ts +46 -0
  38. package/dist/ext/kv/store.d.ts.map +1 -0
  39. package/dist/ext/kv/store.js +256 -0
  40. package/dist/ext/kv/store.js.map +1 -0
  41. package/dist/generated/version-data.d.ts +1 -1
  42. package/dist/generated/version-data.js +2 -2
  43. package/package.json +37 -11
@@ -0,0 +1,168 @@
1
+ /**
2
+ * exec Extension Factory
3
+ *
4
+ * Provides sandboxed command execution via allowlist/blocklist security controls.
5
+ * Each declared command becomes a function with argument validation and process isolation.
6
+ */
7
+ import { runCommand, } from './runner.js';
8
+ // ============================================================
9
+ // FACTORY
10
+ // ============================================================
11
+ /**
12
+ * Create exec extension with sandboxed command execution.
13
+ *
14
+ * Generates one host function per declared command.
15
+ * Each function validates arguments and spawns processes with security controls.
16
+ * Returns dispose() function to abort in-flight processes.
17
+ *
18
+ * @param config - Command definitions and defaults
19
+ * @returns ExtensionResult with command functions and dispose
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const execExt = createExecExtension({
24
+ * commands: {
25
+ * git: {
26
+ * binary: 'git',
27
+ * allowedArgs: ['status', '--short', 'log'],
28
+ * cwd: '/home/user/repo'
29
+ * }
30
+ * }
31
+ * });
32
+ * ```
33
+ */
34
+ export function createExecExtension(config) {
35
+ // Apply defaults
36
+ const globalTimeout = config.timeout ?? 30000; // 30s
37
+ const globalMaxOutputSize = config.maxOutputSize ?? 1048576; // 1MB
38
+ const inheritEnv = config.inheritEnv ?? false;
39
+ // Track in-flight processes for dispose
40
+ const abortControllers = [];
41
+ // Helper: get effective timeout (command-specific or global)
42
+ const getTimeout = (commandConfig) => {
43
+ return commandConfig.timeout ?? globalTimeout;
44
+ };
45
+ // Helper: get effective maxBuffer (command-specific or global)
46
+ const getMaxBuffer = (commandConfig) => {
47
+ return commandConfig.maxBuffer ?? globalMaxOutputSize;
48
+ };
49
+ // Helper: get effective env (merge parent env if inheritEnv, then overlay command env)
50
+ const getEnv = (commandConfig) => {
51
+ if (!inheritEnv && !commandConfig.env) {
52
+ return undefined; // No env needed
53
+ }
54
+ const baseEnv = {};
55
+ // Copy process.env if inheritEnv is true, filtering out undefined
56
+ if (inheritEnv) {
57
+ for (const [key, value] of Object.entries(process.env)) {
58
+ if (value !== undefined) {
59
+ baseEnv[key] = value;
60
+ }
61
+ }
62
+ }
63
+ // Overlay command-specific env
64
+ if (commandConfig.env) {
65
+ Object.assign(baseEnv, commandConfig.env);
66
+ }
67
+ return baseEnv;
68
+ };
69
+ // ============================================================
70
+ // GENERATE COMMAND FUNCTIONS
71
+ // ============================================================
72
+ const functions = {};
73
+ for (const [commandName, commandConfig] of Object.entries(config.commands)) {
74
+ // Create function for this command
75
+ const commandFn = async (args) => {
76
+ // Extract args and stdin from RillValue array
77
+ const argsParam = args[0] ?? [];
78
+ const stdinParam = args[1];
79
+ // Convert args to string array
80
+ const stringArgs = argsParam.map((arg) => String(arg));
81
+ // Create abort controller for this execution
82
+ const controller = new AbortController();
83
+ abortControllers.push(controller);
84
+ try {
85
+ // Build effective config with merged defaults
86
+ const effectiveConfig = {
87
+ ...commandConfig,
88
+ timeout: getTimeout(commandConfig),
89
+ maxBuffer: getMaxBuffer(commandConfig),
90
+ env: getEnv(commandConfig),
91
+ };
92
+ // Execute command
93
+ const result = await runCommand(commandName, effectiveConfig, stringArgs, stdinParam, controller.signal);
94
+ // Return as dict
95
+ return {
96
+ stdout: result.stdout,
97
+ stderr: result.stderr,
98
+ exitCode: result.exitCode,
99
+ };
100
+ }
101
+ finally {
102
+ // Remove from tracking list
103
+ const index = abortControllers.indexOf(controller);
104
+ if (index !== -1) {
105
+ abortControllers.splice(index, 1);
106
+ }
107
+ }
108
+ };
109
+ // Add to functions object with HostFunctionDefinition structure
110
+ functions[commandName] = {
111
+ params: [
112
+ {
113
+ name: 'args',
114
+ type: 'list',
115
+ description: 'Command arguments',
116
+ defaultValue: [],
117
+ },
118
+ {
119
+ name: 'stdin',
120
+ type: 'string',
121
+ description: 'Standard input data',
122
+ defaultValue: '',
123
+ },
124
+ ],
125
+ fn: commandFn,
126
+ description: commandConfig.description ?? `Execute ${commandName} command`,
127
+ returnType: 'dict',
128
+ };
129
+ }
130
+ // ============================================================
131
+ // INTROSPECTION FUNCTION
132
+ // ============================================================
133
+ const commands = async () => {
134
+ const result = [];
135
+ for (const [name, commandConfig] of Object.entries(config.commands)) {
136
+ result.push({
137
+ name,
138
+ description: commandConfig.description ?? '',
139
+ });
140
+ }
141
+ return result;
142
+ };
143
+ functions['commands'] = {
144
+ params: [],
145
+ fn: commands,
146
+ description: 'List all configured commands',
147
+ returnType: 'list',
148
+ };
149
+ // ============================================================
150
+ // DISPOSE FUNCTION
151
+ // ============================================================
152
+ const dispose = async () => {
153
+ // Abort all in-flight processes
154
+ for (const controller of abortControllers) {
155
+ controller.abort();
156
+ }
157
+ // Clear tracking array
158
+ abortControllers.length = 0;
159
+ };
160
+ // ============================================================
161
+ // EXTENSION RESULT
162
+ // ============================================================
163
+ return {
164
+ ...functions,
165
+ dispose,
166
+ };
167
+ }
168
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ext/exec/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * exec Extension Runner
3
+ *
4
+ * Handles process spawning with argument validation and security controls.
5
+ * Uses child_process.execFile() for shell injection prevention.
6
+ */
7
+ /** Command configuration with security controls */
8
+ export interface CommandConfig {
9
+ /** Binary executable path */
10
+ readonly binary: string;
11
+ /** Optional timeout in milliseconds */
12
+ readonly timeout?: number | undefined;
13
+ /** Optional output size limit in bytes */
14
+ readonly maxBuffer?: number | undefined;
15
+ /** Allowed arguments (allowlist mode) */
16
+ readonly allowedArgs?: readonly string[] | undefined;
17
+ /** Blocked arguments (blocklist mode) */
18
+ readonly blockedArgs?: readonly string[] | undefined;
19
+ /** Working directory for command execution */
20
+ readonly cwd?: string | undefined;
21
+ /** Environment variables for command */
22
+ readonly env?: Record<string, string> | undefined;
23
+ /** Whether command accepts stdin */
24
+ readonly stdin?: boolean | undefined;
25
+ /** Optional description for introspection */
26
+ readonly description?: string | undefined;
27
+ }
28
+ /** Command execution result */
29
+ export interface CommandResult {
30
+ readonly stdout: string;
31
+ readonly stderr: string;
32
+ readonly exitCode: number;
33
+ }
34
+ /**
35
+ * Execute command with process spawning and security controls.
36
+ *
37
+ * Uses execFile() to prevent shell injection attacks.
38
+ * Validates arguments against allowlist/blocklist rules.
39
+ * Returns stdout, stderr, and exit code (non-zero exit is not an error).
40
+ *
41
+ * @param commandName - Command name for error messages
42
+ * @param config - Command configuration with security rules
43
+ * @param args - Command arguments
44
+ * @param stdinData - Optional stdin data
45
+ * @param signal - Optional AbortSignal for cancellation
46
+ * @returns Command result with stdout, stderr, and exitCode
47
+ * @throws RuntimeError RILL-R004 for validation failures
48
+ * @throws RuntimeError RILL-R012 for timeout
49
+ * @throws RuntimeError RILL-R004 for output size limit exceeded
50
+ * @throws RuntimeError RILL-R004 for binary not found
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const result = await runCommand('git', {
55
+ * binary: 'git',
56
+ * allowedArgs: ['status', '--short']
57
+ * }, ['status', '--short']);
58
+ * // Returns: { stdout: "...", stderr: "", exitCode: 0 }
59
+ * ```
60
+ */
61
+ export declare function runCommand(commandName: string, config: CommandConfig, args: readonly string[], stdinData?: string | undefined, signal?: AbortSignal | undefined): Promise<CommandResult>;
62
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/ext/exec/runner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,6BAA6B;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,0CAA0C;IAC1C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxC,yCAAyC;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACrD,yCAAyC;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACrD,8CAA8C;IAC9C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,wCAAwC;IACxC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAClD,oCAAoC;IACpC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,6CAA6C;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,+BAA+B;AAC/B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAgFD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,UAAU,CAC9B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,EAC9B,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,GAC/B,OAAO,CAAC,aAAa,CAAC,CAoIxB"}
@@ -0,0 +1,168 @@
1
+ /**
2
+ * exec Extension Runner
3
+ *
4
+ * Handles process spawning with argument validation and security controls.
5
+ * Uses child_process.execFile() for shell injection prevention.
6
+ */
7
+ import { execFile } from 'node:child_process';
8
+ import { promisify } from 'node:util';
9
+ import { RuntimeError } from '../../error-classes.js';
10
+ const execFileAsync = promisify(execFile);
11
+ // ============================================================
12
+ // VALIDATION
13
+ // ============================================================
14
+ /**
15
+ * Validate arguments against allowlist/blocklist rules.
16
+ *
17
+ * @param args - Command arguments to validate
18
+ * @param config - Command configuration with security rules
19
+ * @param commandName - Command name for error messages
20
+ * @throws RuntimeError RILL-R004 if validation fails
21
+ */
22
+ function validateArgs(args, config, commandName) {
23
+ const { allowedArgs, blockedArgs } = config;
24
+ // Allowlist mode: every arg must be in allowedArgs
25
+ if (allowedArgs !== undefined) {
26
+ for (const arg of args) {
27
+ if (!allowedArgs.includes(arg)) {
28
+ // EC-14: Arg not in allowlist
29
+ throw new RuntimeError('RILL-R004', `arg "${arg}" not permitted for command "${commandName}"`, undefined, { commandName, arg, allowedArgs });
30
+ }
31
+ }
32
+ }
33
+ // Blocklist mode: no arg can be in blockedArgs
34
+ if (blockedArgs !== undefined) {
35
+ for (const arg of args) {
36
+ if (blockedArgs.includes(arg)) {
37
+ // EC-15: Arg in blocklist
38
+ throw new RuntimeError('RILL-R004', `arg "${arg}" is blocked for command "${commandName}"`, undefined, { commandName, arg, blockedArgs });
39
+ }
40
+ }
41
+ }
42
+ }
43
+ /**
44
+ * Check if stdin is supported by the command.
45
+ *
46
+ * @param config - Command configuration
47
+ * @param commandName - Command name for error messages
48
+ * @param hasStdin - Whether stdin was provided
49
+ * @throws RuntimeError RILL-R004 if stdin not supported but provided
50
+ */
51
+ function validateStdin(config, commandName, hasStdin) {
52
+ if (hasStdin && !config.stdin) {
53
+ // EC-19: stdin not supported
54
+ throw new RuntimeError('RILL-R004', `command "${commandName}" does not support stdin`, undefined, { commandName });
55
+ }
56
+ }
57
+ // ============================================================
58
+ // EXECUTION
59
+ // ============================================================
60
+ /**
61
+ * Execute command with process spawning and security controls.
62
+ *
63
+ * Uses execFile() to prevent shell injection attacks.
64
+ * Validates arguments against allowlist/blocklist rules.
65
+ * Returns stdout, stderr, and exit code (non-zero exit is not an error).
66
+ *
67
+ * @param commandName - Command name for error messages
68
+ * @param config - Command configuration with security rules
69
+ * @param args - Command arguments
70
+ * @param stdinData - Optional stdin data
71
+ * @param signal - Optional AbortSignal for cancellation
72
+ * @returns Command result with stdout, stderr, and exitCode
73
+ * @throws RuntimeError RILL-R004 for validation failures
74
+ * @throws RuntimeError RILL-R012 for timeout
75
+ * @throws RuntimeError RILL-R004 for output size limit exceeded
76
+ * @throws RuntimeError RILL-R004 for binary not found
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * const result = await runCommand('git', {
81
+ * binary: 'git',
82
+ * allowedArgs: ['status', '--short']
83
+ * }, ['status', '--short']);
84
+ * // Returns: { stdout: "...", stderr: "", exitCode: 0 }
85
+ * ```
86
+ */
87
+ export async function runCommand(commandName, config, args, stdinData, signal) {
88
+ // Validate arguments against security rules
89
+ validateArgs(args, config, commandName);
90
+ // Validate stdin support
91
+ validateStdin(config, commandName, stdinData !== undefined);
92
+ // Prepare execFile options
93
+ const options = {
94
+ encoding: 'utf8',
95
+ };
96
+ if (config.timeout !== undefined) {
97
+ options.timeout = config.timeout;
98
+ }
99
+ if (config.maxBuffer !== undefined) {
100
+ options.maxBuffer = config.maxBuffer;
101
+ }
102
+ // Add cwd if provided
103
+ if (config.cwd !== undefined) {
104
+ options.cwd = config.cwd;
105
+ }
106
+ // Add env if provided
107
+ if (config.env !== undefined) {
108
+ options.env = config.env;
109
+ }
110
+ // Add stdin data if provided
111
+ if (stdinData !== undefined) {
112
+ options.input = stdinData;
113
+ }
114
+ // Add abort signal if provided
115
+ if (signal !== undefined) {
116
+ options.signal = signal;
117
+ }
118
+ try {
119
+ // Execute command using execFile (no shell interpolation)
120
+ const { stdout, stderr } = await execFileAsync(config.binary, args, options);
121
+ return {
122
+ stdout: String(stdout || ''),
123
+ stderr: String(stderr || ''),
124
+ exitCode: 0,
125
+ };
126
+ }
127
+ catch (err) {
128
+ // Handle execution errors
129
+ if (err && typeof err === 'object') {
130
+ const execError = err;
131
+ // EC-16: Binary not found
132
+ if (execError.code === 'ENOENT') {
133
+ throw new RuntimeError('RILL-R004', `binary not found: ${config.binary}`, undefined, { commandName, binary: config.binary });
134
+ }
135
+ // EC-18: Output exceeds limit (check multiple conditions)
136
+ const isMaxBufferError = execError.code === 'ERR_CHILD_PROCESS_STDOUT_MAXBUFFER' ||
137
+ execError.code === 'ERR_CHILD_PROCESS_STDERR_MAXBUFFER' ||
138
+ (execError.message &&
139
+ execError.message.toLowerCase().includes('maxbuffer')) ||
140
+ (execError.killed === true &&
141
+ execError.signal === 'SIGTERM' &&
142
+ config.maxBuffer !== undefined);
143
+ if (isMaxBufferError) {
144
+ throw new RuntimeError('RILL-R004', `command output exceeds size limit`, undefined, { commandName, maxBuffer: config.maxBuffer });
145
+ }
146
+ // EC-17: Timeout (must check after maxBuffer to avoid confusion)
147
+ if (execError.killed === true && execError.signal === 'SIGTERM') {
148
+ const timeoutMs = config.timeout || 0;
149
+ throw new RuntimeError('RILL-R012', `command "${commandName}" timed out (${timeoutMs}ms)`, undefined, { commandName, timeoutMs });
150
+ }
151
+ // Non-zero exit code: return as CommandResult (not an error)
152
+ if ('stdout' in execError && 'stderr' in execError) {
153
+ // Extract exit code from error
154
+ const exitCode = 'code' in err && typeof err.code === 'number'
155
+ ? err.code
156
+ : 1;
157
+ return {
158
+ stdout: String(execError.stdout || ''),
159
+ stderr: String(execError.stderr || ''),
160
+ exitCode,
161
+ };
162
+ }
163
+ }
164
+ // Unknown error: re-throw
165
+ throw err;
166
+ }
167
+ }
168
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/ext/exec/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Fetch Extension Factory
3
+ *
4
+ * Creates host functions for HTTP requests based on endpoint configuration.
5
+ * Scripts call endpoints with positional args or single dict argument.
6
+ * All URLs are constructed from config - scripts cannot specify arbitrary URLs.
7
+ */
8
+ import type { ExtensionResult } from '../../runtime/ext/extensions.js';
9
+ /** Parameter definition for endpoint */
10
+ export interface EndpointParam {
11
+ readonly name: string;
12
+ readonly type: 'string' | 'number' | 'bool' | 'dict';
13
+ readonly required?: boolean | undefined;
14
+ readonly location: 'path' | 'query' | 'body' | 'header';
15
+ readonly defaultValue?: string | number | boolean | undefined;
16
+ }
17
+ /** Endpoint configuration with parameter declarations */
18
+ export interface EndpointConfig {
19
+ readonly method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
20
+ readonly path: string;
21
+ readonly params?: EndpointParam[] | undefined;
22
+ readonly headers?: Record<string, string> | undefined;
23
+ readonly body?: 'json' | 'form' | 'text' | undefined;
24
+ readonly responseFormat?: 'json' | 'text' | undefined;
25
+ readonly responseShape?: 'body' | 'full' | undefined;
26
+ readonly description?: string | undefined;
27
+ }
28
+ /** Fetch extension configuration */
29
+ export interface FetchConfig {
30
+ readonly baseUrl: string;
31
+ readonly headers?: Record<string, string> | (() => Record<string, string>) | undefined;
32
+ readonly timeout?: number | undefined;
33
+ readonly retries?: number | undefined;
34
+ readonly retryDelay?: number | undefined;
35
+ readonly maxConcurrent?: number | undefined;
36
+ readonly responseFormat?: 'json' | 'text' | undefined;
37
+ readonly responseShape?: 'body' | 'full' | undefined;
38
+ readonly endpoints: Record<string, EndpointConfig>;
39
+ }
40
+ /**
41
+ * Create fetch extension with generated endpoint functions.
42
+ *
43
+ * Each endpoint in config becomes a host function.
44
+ * Scripts call endpoints with positional args or single dict.
45
+ * All URLs constructed from config - scripts cannot create arbitrary URLs.
46
+ *
47
+ * @param config - Fetch configuration with endpoints
48
+ * @returns ExtensionResult with endpoint functions and introspection
49
+ * @throws Error on invalid configuration
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const api = createFetchExtension({
54
+ * baseUrl: 'https://api.example.com',
55
+ * endpoints: {
56
+ * getUser: {
57
+ * method: 'GET',
58
+ * path: '/users/:id',
59
+ * params: [
60
+ * { name: 'id', type: 'string', location: 'path' }
61
+ * ]
62
+ * }
63
+ * }
64
+ * });
65
+ * ```
66
+ */
67
+ export declare function createFetchExtension(config: FetchConfig): ExtensionResult;
68
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ext/fetch/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAmBvE,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IACrD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACxD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;CAC/D;AAED,yDAAyD;AACzD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,EAAE,GAAG,SAAS,CAAC;IAC9C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACtD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACtD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,oCAAoC;AACpC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EACb,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACtB,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC9B,SAAS,CAAC;IACd,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACtD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CACpD;AAgID;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,eAAe,CAoKzE"}