@pranshulsoni/flowwatch 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.
Files changed (73) hide show
  1. package/README.md +442 -0
  2. package/dist/ai/groqInsightService.d.ts +39 -0
  3. package/dist/ai/groqInsightService.js +230 -0
  4. package/dist/createFlowwatch.d.ts +17 -0
  5. package/dist/createFlowwatch.js +90 -0
  6. package/dist/dashboard/routes/dashboardResponse.d.ts +204 -0
  7. package/dist/dashboard/routes/dashboardResponse.js +248 -0
  8. package/dist/dashboard/routes/router.d.ts +13 -0
  9. package/dist/dashboard/routes/router.js +708 -0
  10. package/dist/dashboard/static/dashboard.html +6061 -0
  11. package/dist/engine/background/queues/workflowQueue.d.ts +6 -0
  12. package/dist/engine/background/queues/workflowQueue.js +14 -0
  13. package/dist/engine/background/workers/workflowWorker.d.ts +15 -0
  14. package/dist/engine/background/workers/workflowWorker.js +98 -0
  15. package/dist/engine/errors/errorEngine.d.ts +27 -0
  16. package/dist/engine/errors/errorEngine.js +115 -0
  17. package/dist/engine/flags/evaluateFlag.d.ts +3 -0
  18. package/dist/engine/flags/evaluateFlag.js +50 -0
  19. package/dist/engine/flags/flagEngine.d.ts +9 -0
  20. package/dist/engine/flags/flagEngine.js +52 -0
  21. package/dist/engine/flags/hashRollout.d.ts +1 -0
  22. package/dist/engine/flags/hashRollout.js +9 -0
  23. package/dist/engine/flags/types.d.ts +7 -0
  24. package/dist/engine/flags/types.js +1 -0
  25. package/dist/engine/trace/traceEngine.d.ts +26 -0
  26. package/dist/engine/trace/traceEngine.js +76 -0
  27. package/dist/engine/workflows/types.d.ts +28 -0
  28. package/dist/engine/workflows/types.js +1 -0
  29. package/dist/engine/workflows/workflowEngine.d.ts +15 -0
  30. package/dist/engine/workflows/workflowEngine.js +112 -0
  31. package/dist/index.d.ts +9 -0
  32. package/dist/index.js +3 -0
  33. package/dist/persistence/cache/redisClient.d.ts +2 -0
  34. package/dist/persistence/cache/redisClient.js +4 -0
  35. package/dist/persistence/db/postgres.d.ts +3 -0
  36. package/dist/persistence/db/postgres.js +4 -0
  37. package/dist/persistence/migrations/migrationRunner.d.ts +3 -0
  38. package/dist/persistence/migrations/migrationRunner.js +46 -0
  39. package/dist/persistence/migrations/migrations.d.ts +5 -0
  40. package/dist/persistence/migrations/migrations.js +191 -0
  41. package/dist/persistence/repositories/errors/errorRepository.d.ts +38 -0
  42. package/dist/persistence/repositories/errors/errorRepository.js +63 -0
  43. package/dist/persistence/repositories/flags/flagRepository.d.ts +72 -0
  44. package/dist/persistence/repositories/flags/flagRepository.js +245 -0
  45. package/dist/persistence/repositories/traces/traceRepository.d.ts +64 -0
  46. package/dist/persistence/repositories/traces/traceRepository.js +110 -0
  47. package/dist/persistence/repositories/workflows/workflowRepository.d.ts +93 -0
  48. package/dist/persistence/repositories/workflows/workflowRepository.js +260 -0
  49. package/dist/persistence/transaction.d.ts +2 -0
  50. package/dist/persistence/transaction.js +16 -0
  51. package/dist/runtime/config/normalizeConfig.d.ts +2 -0
  52. package/dist/runtime/config/normalizeConfig.js +46 -0
  53. package/dist/runtime/config/validationConfig.d.ts +2 -0
  54. package/dist/runtime/config/validationConfig.js +119 -0
  55. package/dist/runtime/health/healthService.d.ts +30 -0
  56. package/dist/runtime/health/healthService.js +54 -0
  57. package/dist/runtime/tracing/traceContext.d.ts +12 -0
  58. package/dist/runtime/tracing/traceContext.js +28 -0
  59. package/dist/runtime/tracing/tracingMiddleware.d.ts +3 -0
  60. package/dist/runtime/tracing/tracingMiddleware.js +46 -0
  61. package/dist/search/elasticsearch/client.d.ts +2 -0
  62. package/dist/search/elasticsearch/client.js +4 -0
  63. package/dist/search/elasticsearch/indexSetup.d.ts +3 -0
  64. package/dist/search/elasticsearch/indexSetup.js +43 -0
  65. package/dist/search/elasticsearch/indexer.d.ts +9 -0
  66. package/dist/search/elasticsearch/indexer.js +86 -0
  67. package/dist/search/elasticsearch/mappingChecker.d.ts +2 -0
  68. package/dist/search/elasticsearch/mappingChecker.js +28 -0
  69. package/dist/types/index.d.ts +48 -0
  70. package/dist/types/index.js +1 -0
  71. package/dist/utils/flowwatchEnvStore.d.ts +27 -0
  72. package/dist/utils/flowwatchEnvStore.js +145 -0
  73. package/package.json +63 -0
package/README.md ADDED
@@ -0,0 +1,442 @@
1
+ # FlowWatch
2
+
3
+ **The world's first npm package that gives you durable workflows, feature flags, request tracing, and error reporting — all in one, completely free, and running entirely inside your own Express app.**
4
+
5
+ No SaaS. No monthly bill. No third-party cloud. Your Postgres, your Redis, your data.
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ - [Why I built this](#why-i-built-this) ← skip this if you just want the code
12
+ - [What you get with one install](#what-you-get-with-one-install)
13
+ - [Getting started](#getting-started)
14
+ - [Durable Workflows](#durable-workflows)
15
+ - [Feature Flags](#feature-flags)
16
+ - [Request Tracing](#request-tracing)
17
+ - [Error Reporting](#error-reporting)
18
+ - [AI Insights and Chat](#ai-insights-and-chat)
19
+ - [Infrastructure](#infrastructure)
20
+ - [The Dashboard](#the-dashboard)
21
+ - [Quick Reference](#quick-reference)
22
+ - [Database Schema](#database-schema)
23
+ - [License](#license)
24
+
25
+ ---
26
+
27
+ ## Why I built this
28
+
29
+ When I was first learning backend development, I thought building an API was simple. You write an Express route, connect to a database, query some data, and return a JSON response. It felt clean, fast, and empowering.
30
+
31
+ Then, my first real-world project went live.
32
+
33
+ Within weeks, marketing wanted to hide new checkout pages behind a feature flag. The product manager wanted a complex user-onboarding sequence that sent emails, charged cards, and created database records — all guaranteed never to fail midway. Users started complaining about occasional timeouts, and I spent hours staring at thousands of lines of scrambled console logs.
34
+
35
+ To solve these basic production problems, I was forced to stitch together a monster of external SaaS tools:
36
+ 1. **LaunchDarkly** (or Split) for Feature Flags ($$$)
37
+ 2. **Sentry** (or Rollbar) for Error Tracking ($$)
38
+ 3. **Datadog** (or Jaeger) for Distributed Tracing ($$$$)
39
+ 4. **Temporal** (or BullMQ with custom state machines) for Durable Workflows
40
+
41
+ Suddenly, I wasn't just a backend developer writing code; I was a systems integrator managing five different SDKs, logging into five different dashboards, paying five monthly subscriptions, and writing hundreds of lines of glue code. Worst of all, **none of these tools talked to each other**. My Sentry error had no idea about the Temporal workflow step that failed, and my Datadog traces didn't show which LaunchDarkly feature flags were enabled.
42
+
43
+ **FlowWatch is the tool I always wished I had.** It embeds all four pillars of backend operations directly into your Express application as a single npm package. It uses the infrastructure you already have (Postgres, Redis, Elasticsearch) and serves a beautiful, unified admin dashboard directly from your app.
44
+
45
+ And because all your operations data lives in the same database, we added **Groq-powered AI insights and support chat** that can correlate logs, flags, errors, and traces to diagnose your bugs in seconds.
46
+
47
+ ---
48
+
49
+ ## What you get with one install
50
+
51
+ ```bash
52
+ npm install @pranshulsoni/flowwatch
53
+ ```
54
+
55
+ - **Durable Workflows** — define multi-step processes that survive server crashes and retry failed steps automatically
56
+ - **Feature Flags** — toggle features and do percentage rollouts from a dashboard, no redeploys needed
57
+ - **Request Tracing** — see exactly what every request did, how long each part took, and which parts were slow
58
+ - **Error Reporting** — capture, group, and search errors with full stack traces and context
59
+ - **AI Diagnostics** — connect a Groq API key and get automated incident analysis and a chat interface that knows your actual data
60
+ - **Built-in Dashboard** — a 10-page admin UI served directly from your Express app at whatever path you choose
61
+
62
+ Everything stores in your own Postgres database. Redis is optional (but recommended). Elasticsearch is optional. The Groq API key is optional. Postgres is the only hard requirement.
63
+
64
+ ---
65
+
66
+ ## Getting started
67
+
68
+ ### Minimum setup
69
+
70
+ ```ts
71
+ import express from "express";
72
+ import { createFlowwatch } from "@pranshulsoni/flowwatch";
73
+
74
+ const app = express();
75
+ app.use(express.json());
76
+
77
+ const fw = await createFlowwatch({
78
+ db: {
79
+ connectionString: process.env.DATABASE_URL
80
+ },
81
+ migrations: {
82
+ autoRun: true
83
+ },
84
+ runtime: {
85
+ serviceName: "my-api",
86
+ environment: "production"
87
+ }
88
+ });
89
+
90
+ app.use(fw.requestTracer); // goes first
91
+ app.use("/ops", fw.dashboard);
92
+
93
+ // your routes go here
94
+
95
+ app.use(fw.errorHandler); // goes last
96
+
97
+ app.listen(3000);
98
+ ```
99
+
100
+ Visit `http://localhost:3000/ops` and the dashboard is live.
101
+
102
+ ### Full setup with Redis and Elasticsearch
103
+
104
+ ```ts
105
+ const fw = await createFlowwatch({
106
+ db: {
107
+ connectionString: process.env.DATABASE_URL
108
+ },
109
+ redis: {
110
+ url: process.env.REDIS_URL
111
+ },
112
+ elasticsearch: {
113
+ node: process.env.ELASTICSEARCH_URL
114
+ },
115
+ migrations: {
116
+ autoRun: true
117
+ },
118
+ runtime: {
119
+ serviceName: "my-api",
120
+ environment: "production"
121
+ }
122
+ });
123
+ ```
124
+
125
+ ---
126
+
127
+ ## Durable Workflows
128
+
129
+ ### The problem
130
+
131
+ You have a checkout flow. Charge card → deduct inventory → send email → generate invoice. You write it as four sequential awaits in a route handler. It works in development. In production, the email server goes down between step 2 and step 3. The card was charged, inventory was deducted, but the process died halfway.
132
+
133
+ Now you have inconsistent data and no visibility into what happened.
134
+
135
+ ### Without FlowWatch
136
+
137
+ ```ts
138
+ app.post("/checkout", async (req, res) => {
139
+ try {
140
+ await chargeCard(req.body);
141
+ await deductInventory(req.body); // server crashes here
142
+ await sendEmail(req.body); // never runs
143
+ await generateInvoice(req.body);
144
+ res.json({ ok: true });
145
+ } catch (err) {
146
+ // what do you roll back? what already ran?
147
+ res.status(500).json({ error: "something failed" });
148
+ }
149
+ });
150
+ ```
151
+
152
+ ### With FlowWatch
153
+
154
+ ```ts
155
+ fw.workflow("checkout", [
156
+ { name: "charge-card", run: async (input) => chargeCard(input), maxRetries: 3 },
157
+ { name: "deduct-inventory", run: async (input) => deductInventory(input), maxRetries: 2 },
158
+ { name: "send-email", run: async (input) => sendEmail(input), maxRetries: 5 },
159
+ { name: "generate-invoice", run: async (input) => generateInvoice(input), maxRetries: 2 }
160
+ ]);
161
+
162
+ app.post("/checkout", async (req, res) => {
163
+ const { executionId } = await fw.trigger("checkout", req.body);
164
+ res.json({ executionId });
165
+ });
166
+ ```
167
+
168
+ Each step's result is saved to Postgres immediately after it completes. If the server crashes on step 2, the next time it starts up the workflow engine picks up the execution and resumes from step 2. No double charges. No lost orders. No cron jobs.
169
+
170
+ ---
171
+
172
+ ## Feature Flags
173
+
174
+ ### The problem
175
+
176
+ You want to test a new feature with 10% of your users. You hardcode an env variable — now toggling it requires a redeploy. You move it to the database — now every request hits the DB just to read a boolean. You add Redis caching — now you have cache invalidation bugs.
177
+
178
+ ### Without FlowWatch
179
+
180
+ ```ts
181
+ // requires a redeploy to change anything
182
+ if (process.env.NEW_SEARCH === "true") {
183
+ return runNewSearch(query);
184
+ }
185
+ ```
186
+
187
+ ### With FlowWatch
188
+
189
+ ```ts
190
+ const useNewSearch = await fw.flag("new-search-v2", {
191
+ userId: req.user.id,
192
+ email: req.user.email,
193
+ plan: req.user.plan
194
+ });
195
+
196
+ if (useNewSearch) return runNewSearch(req.query.q);
197
+ return runOldSearch(req.query.q);
198
+ ```
199
+
200
+ Toggle flags, adjust rollout percentages, and define targeting rules from the dashboard — no redeploys needed. Evaluations use Redis caching with a 60-second TTL and consistent SHA-256 hashing so the same user always gets the same result.
201
+
202
+ ---
203
+
204
+ ## Request Tracing
205
+
206
+ ### The problem
207
+
208
+ An endpoint is slow. You open your terminal and see 150 log lines from 30 concurrent requests all mixed together. You have no idea which log line belongs to which request or which database call caused the slowdown.
209
+
210
+ ### Without FlowWatch
211
+
212
+ ```ts
213
+ // passing requestId through every function in the codebase
214
+ async function getUser(id: number, requestId: string) {
215
+ console.log(`[${requestId}] fetching user ${id}`);
216
+ return db.query("SELECT * FROM users WHERE id = $1", [id]);
217
+ }
218
+ ```
219
+
220
+ ### With FlowWatch
221
+
222
+ ```ts
223
+ app.use(fw.requestTracer);
224
+
225
+ app.get("/api/dashboard", async (req, res) => {
226
+ const user = await fw.trace("fetch-user", "database", () =>
227
+ db.query("SELECT * FROM users WHERE id = $1", [req.user.id])
228
+ );
229
+
230
+ const rates = await fw.trace("fetch-shipping-api", "http", () =>
231
+ axios.get("https://api.shipping.com/rates")
232
+ );
233
+
234
+ res.json({ user, rates });
235
+ });
236
+ ```
237
+
238
+ FlowWatch uses `AsyncLocalStorage` to carry trace context automatically through every async operation. In the dashboard you get an interactive trace graph:
239
+
240
+ ```
241
+ GET /api/dashboard (4800ms)
242
+ ├── fetch-user database 42ms
243
+ └── fetch-shipping-api http 4720ms ← there's your problem
244
+ ```
245
+
246
+ ---
247
+
248
+ ## Error Reporting
249
+
250
+ ### The problem
251
+
252
+ Something crashes in production. pm2 restarts the server. The stack trace is gone. A user emails support. The same database timeout fires 3,000 times and fills your log file. You have no context about what request or workflow step triggered it.
253
+
254
+ ### Without FlowWatch
255
+
256
+ ```ts
257
+ app.use((err, req, res, next) => {
258
+ console.error(err.stack); // gets lost or floods the log file
259
+ res.status(500).json({ error: "Internal server error" });
260
+ });
261
+ ```
262
+
263
+ ### With FlowWatch
264
+
265
+ ```ts
266
+ // automatically catches all unhandled errors — register last
267
+ app.use(fw.errorHandler);
268
+
269
+ // or capture specific errors manually
270
+ try {
271
+ await processPayment(data);
272
+ } catch (err) {
273
+ await fw.captureError(err, {
274
+ source: "payment_processor",
275
+ level: "fatal",
276
+ category: "dependency"
277
+ });
278
+ }
279
+ ```
280
+
281
+ FlowWatch fingerprints each error using SHA-256 so identical errors are grouped with a frequency count instead of thousands of duplicates. Each error links back to the exact request trace where it happened.
282
+
283
+ ---
284
+
285
+ ## AI Insights and Chat
286
+
287
+ Because FlowWatch holds all four pillars of observability in the same Postgres database, it has a complete picture of your application's state.
288
+
289
+ Connect a **Groq API key** in the Settings page and you get two features:
290
+
291
+ **AI Insights** — one click pulls recent errors, failing workflows, toggled feature flags, and infrastructure health, then returns a structured diagnosis. It can say things like: *"database errors spiked 4 minutes after feature flag 'new-billing-v2' was rolled out to 50%, suggesting the new code path is hitting an unindexed column."*
292
+
293
+ **Ask AI** — a full chat interface where you can ask in plain English:
294
+ - "Which workflow has the most failures this week?"
295
+ - "What's in the stack trace for the checkout error from an hour ago?"
296
+ - "List the feature flags that were toggled today"
297
+
298
+ Both features are optional. Everything else works without a Groq key.
299
+
300
+ ---
301
+
302
+ ## Infrastructure
303
+
304
+ ### Version requirements
305
+
306
+ | Service | Minimum Version | Notes |
307
+ | :--- | :--- | :--- |
308
+ | Postgres | Any modern version (v11+) | No version-specific features used |
309
+ | Redis | **v5+** for workflow queues | Older Redis disables workflows but everything else still works |
310
+ | Elasticsearch | **v8.x** | Built against the v8 API — v7 is not supported |
311
+
312
+ ### Graceful degradation
313
+
314
+ | Service | Required | What breaks without it |
315
+ | :--- | :--- | :--- |
316
+ | Postgres | **Yes** | Nothing works without it |
317
+ | Redis | No | Workflow queues are disabled. Flag evaluations hit Postgres directly |
318
+ | Elasticsearch | No | Search falls back to Postgres queries |
319
+ | Groq API key | No | AI Insights and Ask AI are locked |
320
+
321
+ ### Option 1 — Bring your own URLs
322
+
323
+ ```ts
324
+ const fw = await createFlowwatch({
325
+ db: { connectionString: "postgresql://user:password@your-host:5432/dbname" },
326
+ redis: { url: "redis://your-redis-host:6379" },
327
+ elasticsearch: { node: "https://your-es-host:9200" }
328
+ });
329
+ ```
330
+
331
+ ### Option 2 — Spin up locally with Docker Compose
332
+
333
+ ```bash
334
+ docker-compose up -d
335
+ ```
336
+
337
+ Starts Postgres 16, Redis 7, and Elasticsearch 8.13 locally, then connect with:
338
+
339
+ ```ts
340
+ const fw = await createFlowwatch({
341
+ db: { connectionString: "postgresql://postgres:postgres@localhost:5432/flowwatch" },
342
+ redis: { url: "redis://localhost:6379" },
343
+ elasticsearch: { node: "http://localhost:9200" }
344
+ });
345
+ ```
346
+
347
+ ---
348
+
349
+ ## The Dashboard
350
+
351
+ Once mounted, the dashboard is a 10-page single-page application served directly by your Express router — no React, no build step, no CDN.
352
+
353
+ | Page | What it shows |
354
+ | :--- | :--- |
355
+ | Overview | Summary metrics, recent executions, recent errors, infrastructure health |
356
+ | Workflows | Registered workflow definitions with step counts |
357
+ | Executions | Every workflow run with status, step timeline, input/output |
358
+ | Feature Flags | All flags with toggle switches, rollout percentage bars, rule counts |
359
+ | Errors | Error list with grouping, frequency, category filters, and full-text search |
360
+ | Traces | Interactive SVG trace graph, span inspector, filterable trace table |
361
+ | AI Insights | Automated diagnosis panel (requires Groq key) |
362
+ | Ask AI | Full chat interface with conversation history (requires Groq key) |
363
+ | Health | Live health cards for Postgres, Redis, Elasticsearch |
364
+ | Settings | Environment name, Groq API key, model selection |
365
+
366
+ ---
367
+
368
+ ## Quick Reference
369
+
370
+ ```ts
371
+ // Factory — call once at startup
372
+ const fw = await createFlowwatch(config);
373
+
374
+ // Middleware
375
+ app.use(fw.requestTracer); // mount first
376
+ app.use(fw.errorHandler); // mount last
377
+
378
+ // Dashboard
379
+ app.use("/ops", fw.dashboard);
380
+
381
+ // Workflows
382
+ fw.workflow(name, steps[]);
383
+ await fw.trigger(name, input?); // returns { executionId }
384
+
385
+ // Feature flags
386
+ await fw.flag(key, context?); // returns boolean
387
+
388
+ // Tracing
389
+ await fw.trace(name, type, fn, metadata?);
390
+
391
+ // Error capture
392
+ await fw.captureError(error, options?);
393
+
394
+ // Context helpers (usable anywhere in async call chain)
395
+ getCurrentTraceId();
396
+ getCurrentSpanId();
397
+ getCurrentClientIp();
398
+ ```
399
+
400
+ ---
401
+
402
+ ## Database Schema
403
+
404
+ When you set `migrations: { autoRun: true }`, FlowWatch creates these tables automatically. All table names are prefixed with `flowwatch_` so they never conflict with your own tables.
405
+
406
+ ### Workflows
407
+
408
+ ```
409
+ flowwatch_workflows — id, name, version, timestamps
410
+ flowwatch_workflow_steps — id, workflow_id, step_index, name, max_retries
411
+ flowwatch_workflow_executions — id, workflow_id, status, input, output, error, timestamps
412
+ flowwatch_workflow_step_executions — id, execution_id, step_index, status, input, output, attempt_count, next_retry_at
413
+ ```
414
+
415
+ ### Feature Flags
416
+
417
+ ```
418
+ flowwatch_feature_flags — id, key (unique), enabled, rollout_percentage, timestamps
419
+ flowwatch_feature_flag_rules — id, flag_id, attribute, operator, value, enabled
420
+ flowwatch_feature_flag_audit_logs — id, flag_id, action, before, after, changed_by, created_at
421
+ ```
422
+
423
+ ### Traces and Errors
424
+
425
+ ```
426
+ flowwatch_request_traces — id, method, path, status_code, duration_ms, ip, user_agent, metadata
427
+ flowwatch_trace_spans — id, trace_id, parent_span_id (self-ref), name, type, status, duration_ms
428
+ flowwatch_errors — id, trace_id, span_id, category, level, message, stack, fingerprint (SHA-256)
429
+ ```
430
+
431
+ ### Elasticsearch Indices (optional)
432
+
433
+ | Index | Contents |
434
+ | :--- | :--- |
435
+ | `flowwatch_errors` | All captured errors, mirrored from Postgres for full-text search |
436
+ | `flowwatch_trace_spans` | All trace spans, mirrored from Postgres for fast filtering |
437
+
438
+ ---
439
+
440
+ ## License
441
+
442
+ ISC — free to use, modify, and distribute.
@@ -0,0 +1,39 @@
1
+ export interface FlowwatchAiInsightContext {
2
+ serviceName: string;
3
+ environment: string;
4
+ generatedAt: string;
5
+ workflows: unknown[];
6
+ executions: unknown[];
7
+ errors: unknown[];
8
+ traces: unknown[];
9
+ flags: unknown[];
10
+ health: unknown[];
11
+ }
12
+ export interface FlowwatchAiInsight {
13
+ summary: string;
14
+ likelyCause: string;
15
+ impact: string;
16
+ evidence: string[];
17
+ recommendedActions: string[];
18
+ confidence: number;
19
+ sourceCounts: {
20
+ workflows: number;
21
+ executions: number;
22
+ errors: number;
23
+ traces: number;
24
+ flags: number;
25
+ health: number;
26
+ };
27
+ }
28
+ export interface GroqModelOption {
29
+ id: string;
30
+ ownedBy?: string;
31
+ }
32
+ export declare function isServerObservabilityQuestion(message: string): boolean;
33
+ export declare function outOfScopeFlowwatchResponse(): string;
34
+ export declare function listGroqModels(): Promise<GroqModelOption[]>;
35
+ export declare function generateGroqInsight(context: FlowwatchAiInsightContext, model?: string): Promise<FlowwatchAiInsight>;
36
+ export declare function askGroqAi(context: FlowwatchAiInsightContext, message: string, history: Array<{
37
+ role: string;
38
+ content: string;
39
+ }>, model?: string): Promise<string>;