skedyul 0.3.20 → 1.0.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.
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  The official Node.js SDK for building Skedyul integration apps. This package provides everything you need to create MCP (Model Context Protocol) servers, handle webhooks, manage lifecycle events, and interact with the Skedyul platform.
4
4
 
5
+ ## Version 1.0.0
6
+
7
+ This release introduces a modular, file-based configuration system with improved type safety and developer experience.
8
+
5
9
  ## Features
6
10
 
7
11
  - **MCP Server**: Build tools that AI agents can invoke via the Model Context Protocol
@@ -9,6 +13,7 @@ The official Node.js SDK for building Skedyul integration apps. This package pro
9
13
  - **Lifecycle Hooks**: Handle app installation, provisioning, and cleanup
10
14
  - **Core API Client**: Interact with Skedyul resources (workplaces, channels, instances)
11
15
  - **CLI**: Develop and test locally with hot-reload and tunneling
16
+ - **Modular Config**: File-based configuration with auto-discovery patterns
12
17
 
13
18
  ## Installation
14
19
 
@@ -25,17 +30,76 @@ pnpm add skedyul
25
30
  ```ts
26
31
  // skedyul.config.ts
27
32
  import { defineConfig } from 'skedyul'
33
+ import pkg from './package.json'
28
34
 
29
35
  export default defineConfig({
30
- name: 'my-integration',
31
- version: '1.0.0',
36
+ name: 'My Integration',
37
+ version: pkg.version,
38
+ description: 'Description of what this app does',
32
39
  computeLayer: 'serverless',
33
- tools: import('./src/tools/registry'),
34
- webhooks: import('./src/webhooks/registry'),
40
+
41
+ tools: import('./src/registries'),
42
+ webhooks: import('./src/registries'),
43
+ provision: import('./provision'),
35
44
  })
36
45
  ```
37
46
 
38
- ### 2. Define a tool
47
+ ### 2. Define your provision config
48
+
49
+ ```ts
50
+ // provision.ts
51
+ import type { ProvisionConfig } from 'skedyul'
52
+
53
+ import env from './env'
54
+ import { models, relationships } from './crm'
55
+ import * as channels from './channels'
56
+ import * as pages from './pages'
57
+ import navigation from './pages/navigation'
58
+
59
+ const config: ProvisionConfig = {
60
+ env,
61
+ navigation,
62
+ models: Object.values(models),
63
+ channels: Object.values(channels),
64
+ pages: Object.values(pages),
65
+ relationships,
66
+ }
67
+
68
+ export default config
69
+ ```
70
+
71
+ ### 3. Define a model
72
+
73
+ ```ts
74
+ // crm/models/contact.ts
75
+ import { defineModel } from 'skedyul'
76
+
77
+ export default defineModel({
78
+ handle: 'contact',
79
+ label: 'Contact',
80
+ labelPlural: 'Contacts',
81
+ scope: 'shared',
82
+
83
+ fields: [
84
+ {
85
+ handle: 'name',
86
+ label: 'Name',
87
+ type: 'string',
88
+ required: true,
89
+ owner: 'workplace',
90
+ },
91
+ {
92
+ handle: 'email',
93
+ label: 'Email',
94
+ type: 'string',
95
+ required: false,
96
+ owner: 'workplace',
97
+ },
98
+ ],
99
+ })
100
+ ```
101
+
102
+ ### 4. Define a tool
39
103
 
40
104
  ```ts
41
105
  // src/tools/hello.ts
@@ -65,7 +129,7 @@ export const helloTool: ToolDefinition<Input, Output> = {
65
129
  }
66
130
  ```
67
131
 
68
- ### 3. Start the server
132
+ ### 5. Start the server
69
133
 
70
134
  ```ts
71
135
  // src/server.ts
@@ -95,6 +159,33 @@ await mcpServer.listen(3000)
95
159
  | [Configuration](./docs/configuration.md) | skedyul.config.ts reference |
96
160
  | [Errors](./docs/errors.md) | Error types and handling patterns |
97
161
 
162
+ ## Project Structure
163
+
164
+ The recommended project structure uses modular, file-based configuration:
165
+
166
+ ```
167
+ my-app/
168
+ ├── skedyul.config.ts # App metadata + imports
169
+ ├── provision.ts # Aggregates all modular configs
170
+ ├── env.ts # Environment variables
171
+ ├── crm/
172
+ │ ├── index.ts # Re-exports models + relationships
173
+ │ ├── relationships.ts # Model relationships
174
+ │ └── models/
175
+ │ ├── index.ts
176
+ │ └── contact.ts
177
+ ├── channels/
178
+ │ ├── index.ts
179
+ │ └── phone.ts
180
+ ├── pages/
181
+ │ ├── index.ts
182
+ │ ├── navigation.ts # Root navigation
183
+ │ └── settings/
184
+ │ └── page.ts
185
+ └── src/
186
+ └── registries.ts # Tools and webhooks
187
+ ```
188
+
98
189
  ## Server Modes
99
190
 
100
191
  ### Dedicated (Docker/ECS)
package/dist/.build-stamp CHANGED
@@ -1 +1 @@
1
- 1771902906904
1
+ 1771999780767
@@ -42,6 +42,101 @@ const logger_1 = require("../../server/logger");
42
42
  const auth_1 = require("../utils/auth");
43
43
  const link_1 = require("../utils/link");
44
44
  const config_1 = require("../utils/config");
45
+ const client_1 = require("../../core/client");
46
+ /**
47
+ * Simple MIME type lookup based on file extension.
48
+ */
49
+ function getMimeType(filePath) {
50
+ const ext = path.extname(filePath).toLowerCase();
51
+ const mimeTypes = {
52
+ '.pdf': 'application/pdf',
53
+ '.jpg': 'image/jpeg',
54
+ '.jpeg': 'image/jpeg',
55
+ '.png': 'image/png',
56
+ '.gif': 'image/gif',
57
+ '.webp': 'image/webp',
58
+ '.svg': 'image/svg+xml',
59
+ '.txt': 'text/plain',
60
+ '.html': 'text/html',
61
+ '.htm': 'text/html',
62
+ '.css': 'text/css',
63
+ '.js': 'application/javascript',
64
+ '.json': 'application/json',
65
+ '.xml': 'application/xml',
66
+ '.csv': 'text/csv',
67
+ '.doc': 'application/msword',
68
+ '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
69
+ '.xls': 'application/vnd.ms-excel',
70
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
71
+ '.zip': 'application/zip',
72
+ '.mp3': 'audio/mpeg',
73
+ '.mp4': 'video/mp4',
74
+ '.wav': 'audio/wav',
75
+ };
76
+ return mimeTypes[ext] || 'application/octet-stream';
77
+ }
78
+ /**
79
+ * Upload a local file and return its file ID.
80
+ */
81
+ async function uploadLocalFile(filePath) {
82
+ const absolutePath = path.resolve(filePath);
83
+ if (!fs.existsSync(absolutePath)) {
84
+ throw new Error(`File not found: ${absolutePath}`);
85
+ }
86
+ const content = fs.readFileSync(absolutePath);
87
+ const fileName = path.basename(absolutePath);
88
+ const mimeType = getMimeType(absolutePath);
89
+ console.error(`Uploading file: ${fileName} (${mimeType}, ${content.length} bytes)...`);
90
+ const result = await client_1.file.upload({
91
+ content,
92
+ name: fileName,
93
+ mimeType,
94
+ });
95
+ console.error(`Uploaded: ${fileName} -> ${result.id}`);
96
+ return result.id;
97
+ }
98
+ /**
99
+ * Process upload templates in args object.
100
+ * Recursively scans all string values and replaces {{upload:/path/to/file}} patterns
101
+ * with the uploaded file ID.
102
+ */
103
+ async function processUploadTemplates(args) {
104
+ const result = {};
105
+ for (const [key, value] of Object.entries(args)) {
106
+ if (typeof value === 'string') {
107
+ const match = value.match(/^\{\{upload:(.+)\}\}$/);
108
+ if (match) {
109
+ const filePath = match[1];
110
+ const fileId = await uploadLocalFile(filePath);
111
+ result[key] = fileId;
112
+ }
113
+ else {
114
+ result[key] = value;
115
+ }
116
+ }
117
+ else if (Array.isArray(value)) {
118
+ result[key] = await Promise.all(value.map(async (item) => {
119
+ if (typeof item === 'string') {
120
+ const match = item.match(/^\{\{upload:(.+)\}\}$/);
121
+ if (match) {
122
+ return await uploadLocalFile(match[1]);
123
+ }
124
+ }
125
+ else if (item && typeof item === 'object') {
126
+ return await processUploadTemplates(item);
127
+ }
128
+ return item;
129
+ }));
130
+ }
131
+ else if (value && typeof value === 'object') {
132
+ result[key] = await processUploadTemplates(value);
133
+ }
134
+ else {
135
+ result[key] = value;
136
+ }
137
+ }
138
+ return result;
139
+ }
45
140
  /**
46
141
  * Find available linked workplaces from .skedyul/links/
47
142
  */
@@ -73,6 +168,7 @@ Arguments:
73
168
  Options:
74
169
  --registry, -r Path to the registry file (default: auto-detected)
75
170
  --args, -a JSON string of arguments to pass to the tool
171
+ Supports {{upload:/path/to/file}} syntax for file uploads
76
172
  --env, -e Set environment variable (can be used multiple times)
77
173
  Format: --env KEY=VALUE
78
174
  --env-file Load environment variables from a file (e.g., .env.local)
@@ -83,6 +179,10 @@ Workplace Options:
83
179
  --workplace, -w Workplace subdomain (auto-detected if only one is linked)
84
180
  Loads env vars from .skedyul/env/{workplace}.env
85
181
 
182
+ File Upload Syntax:
183
+ Use {{upload:/path/to/file}} in any string field within --args to automatically
184
+ upload a local file and replace the template with the uploaded file ID.
185
+
86
186
  Examples:
87
187
  # Basic invocation (auto-detects workspace if only one is linked)
88
188
  skedyul dev invoke appointment_types_list
@@ -93,6 +193,14 @@ Examples:
93
193
  # With arguments
94
194
  skedyul dev invoke create_booking --args '{"date": "2024-01-15"}'
95
195
 
196
+ # With file upload (uploads file and injects file_id)
197
+ skedyul dev invoke parse_lab_report \\
198
+ --args '{"file_id": "{{upload:/path/to/report.pdf}}"}'
199
+
200
+ # Multiple file uploads in one command
201
+ skedyul dev invoke process_documents \\
202
+ --args '{"doc": "{{upload:./doc.pdf}}", "image": "{{upload:./photo.jpg}}"}'
203
+
96
204
  # With inline environment variables
97
205
  skedyul dev invoke api_call \\
98
206
  --args '{"endpoint": "/users"}' \\
@@ -209,12 +317,27 @@ async function invokeCommand(args) {
209
317
  const tokenResponse = await (0, auth_1.callCliApi)({ serverUrl: linkConfig.serverUrl, token: credentials.token }, '/token', { appInstallationId: linkConfig.appInstallationId });
210
318
  workplaceToken = tokenResponse.token;
211
319
  env.SKEDYUL_API_TOKEN = workplaceToken;
320
+ // Configure the skedyul client for file uploads
321
+ (0, client_1.configure)({
322
+ baseUrl: linkConfig.serverUrl,
323
+ apiToken: workplaceToken,
324
+ });
212
325
  }
213
326
  catch (error) {
214
327
  console.error(`Failed to get API token: ${error instanceof Error ? error.message : String(error)}`);
215
328
  process.exit(1);
216
329
  }
217
330
  }
331
+ // Process upload templates in args (e.g., {{upload:/path/to/file}})
332
+ if (isLinked) {
333
+ try {
334
+ toolArgs = await processUploadTemplates(toolArgs);
335
+ }
336
+ catch (error) {
337
+ console.error(`Error processing file uploads: ${error instanceof Error ? error.message : String(error)}`);
338
+ process.exit(1);
339
+ }
340
+ }
218
341
  // Check for estimate mode
219
342
  const estimateMode = Boolean(flags.estimate);
220
343
  // Load registry
@@ -90,9 +90,14 @@ function parseConfigFromSource(configPath) {
90
90
  }
91
91
  // Fallback: try to extract handle from config file (top-level only)
92
92
  if (!handle) {
93
- const handleMatch = content.match(/^\s*handle\s*:\s*['"`]([^'"`]+)['"`]/m);
93
+ const handleMatch = content.match(/^\s{0,2}handle\s*:\s*['"`]([^'"`]+)['"`]/m);
94
94
  handle = handleMatch?.[1] ?? null;
95
95
  }
96
+ // If handle was found in config, prefer it over package.json derived handle
97
+ const configHandleMatch = content.match(/^\s{0,2}handle\s*:\s*['"`]([^'"`]+)['"`]/m);
98
+ if (configHandleMatch?.[1]) {
99
+ handle = configHandleMatch[1];
100
+ }
96
101
  if (!handle && !nameMatch) {
97
102
  return null;
98
103
  }
@@ -10,7 +10,8 @@ import type { ResourceDependency } from './resource';
10
10
  /**
11
11
  * Field data types (lowercase).
12
12
  * - 'string': Short text (single line)
13
- * - 'text': Long text (multi-line)
13
+ * - 'long_string': Long text (multi-line, stored as text in DB)
14
+ * - 'text': Alias for long_string
14
15
  * - 'number': Numeric value
15
16
  * - 'boolean': True/false
16
17
  * - 'date': Date only (no time)
@@ -21,7 +22,7 @@ import type { ResourceDependency } from './resource';
21
22
  * - 'relation': Reference to another model
22
23
  * - 'object': JSON object
23
24
  */
24
- export type FieldType = 'string' | 'text' | 'number' | 'boolean' | 'date' | 'datetime' | 'time' | 'file' | 'image' | 'relation' | 'object';
25
+ export type FieldType = 'string' | 'long_string' | 'text' | 'number' | 'boolean' | 'date' | 'datetime' | 'time' | 'file' | 'image' | 'relation' | 'object';
25
26
  /**
26
27
  * Relationship cardinality between models.
27
28
  */
@@ -4,7 +4,7 @@
4
4
  * Pages define the UI screens for an app, including their
5
5
  * layout, data context, and navigation.
6
6
  */
7
- import type { BaseDefinition } from './base';
7
+ import type { BaseDefinition, StructuredFilter } from './base';
8
8
  import type { ContextDefinition } from './context';
9
9
  import type { ActionDefinition, BlockDefinition } from './form';
10
10
  import type { NavigationConfig } from './navigation';
@@ -14,6 +14,16 @@ import type { NavigationConfig } from './navigation';
14
14
  * - 'list': Shows multiple records (e.g., /phone-numbers)
15
15
  */
16
16
  export type PageType = 'instance' | 'list';
17
+ /**
18
+ * Page filter for list pages.
19
+ * Defines which model and optional filter criteria to use.
20
+ */
21
+ export interface PageFilter {
22
+ /** Model handle to filter */
23
+ model: string;
24
+ /** Optional filter criteria */
25
+ where?: StructuredFilter;
26
+ }
17
27
  /**
18
28
  * Page definition.
19
29
  */
@@ -37,4 +47,6 @@ export interface PageDefinition extends BaseDefinition {
37
47
  actions?: ActionDefinition[];
38
48
  /** Context data to load for Liquid templates */
39
49
  context?: ContextDefinition;
50
+ /** Filter for list pages - defines which model instances to show */
51
+ filter?: PageFilter;
40
52
  }
@@ -382,6 +382,21 @@ export interface FileUrlResponse {
382
382
  /** ISO timestamp when the URL expires */
383
383
  expiresAt: string;
384
384
  }
385
+ /**
386
+ * Response from file.get
387
+ */
388
+ export interface FileInfo {
389
+ /** File ID (fl_xxx format) */
390
+ id: string;
391
+ /** Original filename */
392
+ name: string;
393
+ /** MIME type of the file */
394
+ mimeType: string;
395
+ /** File size in bytes */
396
+ size: number;
397
+ /** ISO timestamp when the file was created */
398
+ createdAt: string;
399
+ }
385
400
  /**
386
401
  * Parameters for file.upload
387
402
  */
@@ -405,6 +420,22 @@ export interface FileUploadResult {
405
420
  url: string | null;
406
421
  }
407
422
  export declare const file: {
423
+ /**
424
+ * Get file metadata by ID.
425
+ *
426
+ * Returns file information including name, mimeType, and size.
427
+ * Files are validated to ensure they belong to the requesting app installation.
428
+ *
429
+ * @example
430
+ * ```ts
431
+ * // Get file info
432
+ * const fileInfo = await file.get('fl_abc123')
433
+ * console.log(fileInfo.name) // 'document.pdf'
434
+ * console.log(fileInfo.mimeType) // 'application/pdf'
435
+ * console.log(fileInfo.size) // 12345
436
+ * ```
437
+ */
438
+ get(fileId: string): Promise<FileInfo>;
408
439
  /**
409
440
  * Get a temporary download URL for an app-scoped file.
410
441
  *
@@ -5,6 +5,7 @@ exports.runWithConfig = runWithConfig;
5
5
  exports.configure = configure;
6
6
  exports.getConfig = getConfig;
7
7
  const async_hooks_1 = require("async_hooks");
8
+ const v4_1 = require("zod/v4");
8
9
  /**
9
10
  * AsyncLocalStorage for request-scoped configuration.
10
11
  * This allows each request to have its own config without affecting other concurrent requests.
@@ -406,6 +407,27 @@ exports.token = {
406
407
  },
407
408
  };
408
409
  exports.file = {
410
+ /**
411
+ * Get file metadata by ID.
412
+ *
413
+ * Returns file information including name, mimeType, and size.
414
+ * Files are validated to ensure they belong to the requesting app installation.
415
+ *
416
+ * @example
417
+ * ```ts
418
+ * // Get file info
419
+ * const fileInfo = await file.get('fl_abc123')
420
+ * console.log(fileInfo.name) // 'document.pdf'
421
+ * console.log(fileInfo.mimeType) // 'application/pdf'
422
+ * console.log(fileInfo.size) // 12345
423
+ * ```
424
+ */
425
+ async get(fileId) {
426
+ const { data } = await callCore('file.get', {
427
+ fileId,
428
+ });
429
+ return data;
430
+ },
409
431
  /**
410
432
  * Get a temporary download URL for an app-scoped file.
411
433
  *
@@ -642,60 +664,11 @@ exports.contactAssociationLink = {
642
664
  },
643
665
  };
644
666
  /**
645
- * Convert a Zod schema to JSON Schema format for transport.
646
- * This is a simplified conversion that handles common Zod types.
667
+ * Convert a Zod schema to JSON Schema format for transport using Zod's built-in conversion.
668
+ * Uses z.toJSONSchema() from Zod 4 for accurate schema conversion.
647
669
  */
648
670
  function zodSchemaToJsonSchema(schema) {
649
- // Use Zod's built-in JSON Schema conversion if available
650
- // For now, we'll pass the schema description and let the server handle it
651
- const jsonSchema = schema._def;
652
- // Basic type mapping
653
- if (jsonSchema?.typeName === 'ZodString') {
654
- return { type: 'string' };
655
- }
656
- if (jsonSchema?.typeName === 'ZodNumber') {
657
- return { type: 'number' };
658
- }
659
- if (jsonSchema?.typeName === 'ZodBoolean') {
660
- return { type: 'boolean' };
661
- }
662
- if (jsonSchema?.typeName === 'ZodArray') {
663
- const arrayDef = jsonSchema;
664
- return {
665
- type: 'array',
666
- items: arrayDef.type ? zodSchemaToJsonSchema(arrayDef.type) : {},
667
- };
668
- }
669
- if (jsonSchema?.typeName === 'ZodObject') {
670
- const objectDef = jsonSchema;
671
- const shape = objectDef.shape?.() ?? {};
672
- const properties = {};
673
- const required = [];
674
- for (const [key, value] of Object.entries(shape)) {
675
- properties[key] = zodSchemaToJsonSchema(value);
676
- // Check if field is required (not optional/nullable)
677
- const valueDef = value._def;
678
- if (valueDef?.typeName !== 'ZodOptional' && valueDef?.typeName !== 'ZodNullable') {
679
- required.push(key);
680
- }
681
- }
682
- return {
683
- type: 'object',
684
- properties,
685
- required: required.length > 0 ? required : undefined,
686
- };
687
- }
688
- if (jsonSchema?.typeName === 'ZodNullable' || jsonSchema?.typeName === 'ZodOptional') {
689
- const innerDef = jsonSchema;
690
- const inner = innerDef.innerType ? zodSchemaToJsonSchema(innerDef.innerType) : {};
691
- return { ...inner, nullable: true };
692
- }
693
- if (jsonSchema?.typeName === 'ZodEnum') {
694
- const enumDef = jsonSchema;
695
- return { type: 'string', enum: enumDef.values };
696
- }
697
- // Fallback: return empty object schema
698
- return { type: 'object' };
671
+ return v4_1.z.toJSONSchema(schema);
699
672
  }
700
673
  exports.ai = {
701
674
  /**
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export { InstallError, MissingRequiredFieldError, AuthenticationError, InvalidCo
8
8
  export type { InstallErrorCode } from './errors';
9
9
  export { z };
10
10
  export { workplace, communicationChannel, instance, token, file, webhook, resource, contactAssociationLink, ai, configure, getConfig, runWithConfig, } from './core/client';
11
- export type { InstanceContext, InstanceData, InstanceMeta, InstancePagination, InstanceListResult, InstanceListArgs, FileUrlResponse, FileUploadParams, FileUploadResult, WebhookCreateResult, WebhookListItem, WebhookDeleteByNameOptions, WebhookListOptions, ResourceLinkParams, ResourceLinkResult, ContactAssociationLinkCreateParams, ContactAssociationLinkCreateResult, AITextContent, AIFileContent, AIImageContent, AIMessageContent, AIMessage, GenerateObjectOptions, GenerateObjectResult, } from './core/client';
11
+ export type { InstanceContext, InstanceData, InstanceMeta, InstancePagination, InstanceListResult, InstanceListArgs, FileInfo, FileUrlResponse, FileUploadParams, FileUploadResult, WebhookCreateResult, WebhookListItem, WebhookDeleteByNameOptions, WebhookListOptions, ResourceLinkParams, ResourceLinkResult, ContactAssociationLinkCreateParams, ContactAssociationLinkCreateResult, AITextContent, AIFileContent, AIImageContent, AIMessageContent, AIMessage, GenerateObjectOptions, GenerateObjectResult, } from './core/client';
12
12
  export { createContextLogger } from './server/logger';
13
13
  export type { ContextLogger } from './server/logger';
14
14
  declare const _default: {
package/dist/schemas.d.ts CHANGED
@@ -1833,7 +1833,7 @@ export declare const ListBlockDefinitionSchema: z.ZodObject<{
1833
1833
  }, z.core.$strip>;
1834
1834
  /** Model mapper block definition - for mapping SHARED models to workspace models */
1835
1835
  export declare const ModelMapperBlockDefinitionSchema: z.ZodObject<{
1836
- type: z.ZodLiteral<"model-mapper">;
1836
+ type: z.ZodLiteral<"model_mapper">;
1837
1837
  model: z.ZodString;
1838
1838
  }, z.core.$strip>;
1839
1839
  /** Union of all block types */
@@ -2210,7 +2210,7 @@ export declare const PageBlockDefinitionSchema: z.ZodUnion<readonly [z.ZodObject
2210
2210
  icon: z.ZodOptional<z.ZodString>;
2211
2211
  emptyMessage: z.ZodOptional<z.ZodString>;
2212
2212
  }, z.core.$strip>, z.ZodObject<{
2213
- type: z.ZodLiteral<"model-mapper">;
2213
+ type: z.ZodLiteral<"model_mapper">;
2214
2214
  model: z.ZodString;
2215
2215
  }, z.core.$strip>]>;
2216
2216
  /** Mode for context data fetching */
@@ -2714,7 +2714,7 @@ export declare const PageDefinitionSchema: z.ZodObject<{
2714
2714
  icon: z.ZodOptional<z.ZodString>;
2715
2715
  emptyMessage: z.ZodOptional<z.ZodString>;
2716
2716
  }, z.core.$strip>, z.ZodObject<{
2717
- type: z.ZodLiteral<"model-mapper">;
2717
+ type: z.ZodLiteral<"model_mapper">;
2718
2718
  model: z.ZodString;
2719
2719
  }, z.core.$strip>]>>;
2720
2720
  actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -3505,7 +3505,7 @@ export declare const ProvisionConfigSchema: z.ZodObject<{
3505
3505
  icon: z.ZodOptional<z.ZodString>;
3506
3506
  emptyMessage: z.ZodOptional<z.ZodString>;
3507
3507
  }, z.core.$strip>, z.ZodObject<{
3508
- type: z.ZodLiteral<"model-mapper">;
3508
+ type: z.ZodLiteral<"model_mapper">;
3509
3509
  model: z.ZodString;
3510
3510
  }, z.core.$strip>]>>;
3511
3511
  actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -4143,7 +4143,7 @@ export declare const SkedyulConfigSchema: z.ZodObject<{
4143
4143
  icon: z.ZodOptional<z.ZodString>;
4144
4144
  emptyMessage: z.ZodOptional<z.ZodString>;
4145
4145
  }, z.core.$strip>, z.ZodObject<{
4146
- type: z.ZodLiteral<"model-mapper">;
4146
+ type: z.ZodLiteral<"model_mapper">;
4147
4147
  model: z.ZodString;
4148
4148
  }, z.core.$strip>]>>;
4149
4149
  actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
package/dist/schemas.js CHANGED
@@ -545,7 +545,7 @@ exports.ListBlockDefinitionSchema = v4_1.z.object({
545
545
  });
546
546
  /** Model mapper block definition - for mapping SHARED models to workspace models */
547
547
  exports.ModelMapperBlockDefinitionSchema = v4_1.z.object({
548
- type: v4_1.z.literal('model-mapper'),
548
+ type: v4_1.z.literal('model_mapper'),
549
549
  /** The SHARED model handle from install config (e.g., "client", "patient") */
550
550
  model: v4_1.z.string(),
551
551
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skedyul",
3
- "version": "0.3.20",
3
+ "version": "1.0.2",
4
4
  "description": "The Skedyul SDK for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",