skedyul 0.1.56 → 0.1.58

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/dist/.build-stamp CHANGED
@@ -1 +1 @@
1
- 1768803609286
1
+ 1769031652921
package/dist/config.d.ts CHANGED
@@ -271,7 +271,7 @@ export type PageType = 'INSTANCE' | 'LIST';
271
271
  /** Block types available for pages */
272
272
  export type PageBlockType = 'form' | 'spreadsheet' | 'kanban' | 'calendar' | 'link';
273
273
  /** Supported field datatypes for page fields */
274
- export type PageFieldType = 'STRING' | 'FILE' | 'NUMBER' | 'DATE' | 'BOOLEAN' | 'SELECT';
274
+ export type PageFieldType = 'STRING' | 'FILE' | 'NUMBER' | 'DATE' | 'BOOLEAN' | 'SELECT' | 'FORM';
275
275
  /** Data source for prepopulating a page field from a model */
276
276
  export interface PageFieldSource {
277
277
  /** Model handle to pull data from */
@@ -279,6 +279,13 @@ export interface PageFieldSource {
279
279
  /** Field handle on that model */
280
280
  field: string;
281
281
  }
282
+ /** Header configuration for FORM type fields */
283
+ export interface PageFormHeader {
284
+ /** Modal title */
285
+ title: string;
286
+ /** Optional description shown below title */
287
+ description?: string;
288
+ }
282
289
  /** Self-contained field definition for page blocks */
283
290
  export interface PageFieldDefinition {
284
291
  /** Unique handle for this field */
@@ -291,7 +298,7 @@ export interface PageFieldDefinition {
291
298
  description?: string;
292
299
  /** Whether field is required */
293
300
  required?: boolean;
294
- /** Tool name from registry to call on value change */
301
+ /** Tool name from registry to call on value change or form submit */
295
302
  handler?: string;
296
303
  /** Data source for prepopulating field value */
297
304
  source?: PageFieldSource;
@@ -302,6 +309,12 @@ export interface PageFieldDefinition {
302
309
  }>;
303
310
  /** For FILE type: accepted file extensions */
304
311
  accept?: string;
312
+ /** For FORM type: header configuration for the modal */
313
+ header?: PageFormHeader;
314
+ /** For FORM type: nested fields rendered inside the modal form */
315
+ fields?: PageFieldDefinition[];
316
+ /** For FORM type: action buttons in the modal footer */
317
+ actions?: PageActionDefinition[];
305
318
  }
306
319
  /** Action button definition for pages */
307
320
  export interface PageActionDefinition {
@@ -170,26 +170,40 @@ export interface InstanceListResult {
170
170
  export interface InstanceListArgs {
171
171
  page?: number;
172
172
  limit?: number;
173
- filters?: Record<string, unknown>;
173
+ /** Filter conditions. Simple format { field: value } or structured { field: { eq: value } } */
174
+ filter?: Record<string, unknown>;
174
175
  }
175
176
  export declare const instance: {
176
177
  /**
177
- * List instances of an internal model scoped by appInstallationId.
178
+ * List instances of an internal model.
179
+ *
180
+ * **Behavior based on context:**
181
+ * - With ctx (appInstallationId): scoped to that installation
182
+ * - Without ctx + sk_app_ token: searches across ALL installations for the app
178
183
  *
179
184
  * @example
180
185
  * ```ts
186
+ * // Scoped to a specific installation (in tool handlers)
181
187
  * const ctx = {
182
- * appInstallationId: fieldContext.appInstallationId,
183
- * workplace: fieldContext.workplace,
188
+ * appInstallationId: context.appInstallationId,
189
+ * workplace: context.workplace,
184
190
  * }
185
- *
186
- * const { data: records, pagination } = await instance.list('compliance_record', ctx, {
191
+ * const { data, pagination } = await instance.list('compliance_record', ctx, {
187
192
  * page: 1,
188
193
  * limit: 10,
189
194
  * })
195
+ *
196
+ * // Cross-installation search (in webhooks with sk_app_ token)
197
+ * const { data } = await instance.list('phone_number', undefined, {
198
+ * filter: { phone: '+1234567890' },
199
+ * })
200
+ * if (data.length > 0) {
201
+ * const { appInstallationId } = data[0]
202
+ * const { token: scopedToken } = await token.exchange(appInstallationId)
203
+ * }
190
204
  * ```
191
205
  */
192
- list(modelHandle: string, ctx: InstanceContext, args?: InstanceListArgs): Promise<InstanceListResult>;
206
+ list(modelHandle: string, ctx?: InstanceContext, args?: InstanceListArgs): Promise<InstanceListResult>;
193
207
  /**
194
208
  * Get a single instance by ID.
195
209
  *
@@ -224,4 +238,29 @@ export declare const instance: {
224
238
  */
225
239
  update(id: string, data: Record<string, unknown>, ctx: InstanceContext): Promise<InstanceData>;
226
240
  };
241
+ export declare const token: {
242
+ /**
243
+ * Exchange an sk_app_ token for an installation-scoped sk_wkp_ JWT.
244
+ *
245
+ * **Requires sk_app_ token** - only works with app-level tokens.
246
+ * Used after identifying the target installation (e.g., via instance.search).
247
+ *
248
+ * The returned JWT is short-lived (1 hour) and scoped to the specific installation.
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * // After finding the installation via instance.search
253
+ * const { token: scopedToken } = await token.exchange(appInstallationId)
254
+ *
255
+ * // Use the scoped token for subsequent operations
256
+ * runWithConfig({ apiToken: scopedToken, baseUrl: config.baseUrl }, async () => {
257
+ * const channels = await communicationChannel.list({ filter: { identifierValue: phoneNumber } })
258
+ * // ...
259
+ * })
260
+ * ```
261
+ */
262
+ exchange(appInstallationId: string): Promise<{
263
+ token: string;
264
+ }>;
265
+ };
227
266
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.instance = exports.communicationChannel = exports.workplace = void 0;
3
+ exports.token = exports.instance = exports.communicationChannel = exports.workplace = void 0;
4
4
  exports.runWithConfig = runWithConfig;
5
5
  exports.configure = configure;
6
6
  exports.getConfig = getConfig;
@@ -81,7 +81,8 @@ function getConfig() {
81
81
  // Core API Client
82
82
  // ─────────────────────────────────────────────────────────────────────────────
83
83
  async function callCore(method, params) {
84
- const { baseUrl, apiToken } = getEffectiveConfig();
84
+ const effectiveConfig = getEffectiveConfig();
85
+ const { baseUrl, apiToken } = effectiveConfig;
85
86
  if (!baseUrl) {
86
87
  throw new Error('Skedyul client not configured: missing baseUrl. Set SKEDYUL_API_URL environment variable or call configure().');
87
88
  }
@@ -92,11 +93,19 @@ async function callCore(method, params) {
92
93
  'Content-Type': 'application/json',
93
94
  Authorization: `Bearer ${apiToken}`,
94
95
  };
95
- const response = await fetch(`${baseUrl}/core`, {
96
+ const fetchUrl = `${baseUrl}/core`;
97
+ const response = await fetch(fetchUrl, {
96
98
  method: 'POST',
97
99
  headers,
98
100
  body: JSON.stringify({ method, params }),
99
101
  });
102
+ // Check content-type before parsing
103
+ const contentType = response.headers.get('content-type') ?? '';
104
+ if (!contentType.includes('application/json')) {
105
+ const text = await response.text();
106
+ console.error(`[skedyul-node] Core API error: ${response.status} - ${text.slice(0, 200)}`);
107
+ throw new Error(`Core API returned non-JSON response (${response.status}): ${text.slice(0, 100)}`);
108
+ }
100
109
  const payload = (await response.json());
101
110
  if (!response.ok) {
102
111
  throw new Error(payload?.error?.message ?? `Core API error (${response.status})`);
@@ -170,29 +179,42 @@ exports.communicationChannel = {
170
179
  };
171
180
  exports.instance = {
172
181
  /**
173
- * List instances of an internal model scoped by appInstallationId.
182
+ * List instances of an internal model.
183
+ *
184
+ * **Behavior based on context:**
185
+ * - With ctx (appInstallationId): scoped to that installation
186
+ * - Without ctx + sk_app_ token: searches across ALL installations for the app
174
187
  *
175
188
  * @example
176
189
  * ```ts
190
+ * // Scoped to a specific installation (in tool handlers)
177
191
  * const ctx = {
178
- * appInstallationId: fieldContext.appInstallationId,
179
- * workplace: fieldContext.workplace,
192
+ * appInstallationId: context.appInstallationId,
193
+ * workplace: context.workplace,
180
194
  * }
181
- *
182
- * const { data: records, pagination } = await instance.list('compliance_record', ctx, {
195
+ * const { data, pagination } = await instance.list('compliance_record', ctx, {
183
196
  * page: 1,
184
197
  * limit: 10,
185
198
  * })
199
+ *
200
+ * // Cross-installation search (in webhooks with sk_app_ token)
201
+ * const { data } = await instance.list('phone_number', undefined, {
202
+ * filter: { phone: '+1234567890' },
203
+ * })
204
+ * if (data.length > 0) {
205
+ * const { appInstallationId } = data[0]
206
+ * const { token: scopedToken } = await token.exchange(appInstallationId)
207
+ * }
186
208
  * ```
187
209
  */
188
210
  async list(modelHandle, ctx, args) {
189
211
  const payload = (await callCore('instance.list', {
190
212
  modelHandle,
191
- appInstallationId: ctx.appInstallationId,
192
- workplaceId: ctx.workplace.id,
213
+ ...(ctx?.appInstallationId ? { appInstallationId: ctx.appInstallationId } : {}),
214
+ ...(ctx?.workplace?.id ? { workplaceId: ctx.workplace.id } : {}),
193
215
  ...(args?.page !== undefined ? { page: args.page } : {}),
194
216
  ...(args?.limit !== undefined ? { limit: args.limit } : {}),
195
- ...(args?.filters ? { filters: args.filters } : {}),
217
+ ...(args?.filter ? { filter: args.filter } : {}),
196
218
  }));
197
219
  return payload;
198
220
  },
@@ -253,3 +275,34 @@ exports.instance = {
253
275
  return payload.instance;
254
276
  },
255
277
  };
278
+ // ─────────────────────────────────────────────────────────────────────────────
279
+ // Token Client
280
+ // ─────────────────────────────────────────────────────────────────────────────
281
+ exports.token = {
282
+ /**
283
+ * Exchange an sk_app_ token for an installation-scoped sk_wkp_ JWT.
284
+ *
285
+ * **Requires sk_app_ token** - only works with app-level tokens.
286
+ * Used after identifying the target installation (e.g., via instance.search).
287
+ *
288
+ * The returned JWT is short-lived (1 hour) and scoped to the specific installation.
289
+ *
290
+ * @example
291
+ * ```ts
292
+ * // After finding the installation via instance.search
293
+ * const { token: scopedToken } = await token.exchange(appInstallationId)
294
+ *
295
+ * // Use the scoped token for subsequent operations
296
+ * runWithConfig({ apiToken: scopedToken, baseUrl: config.baseUrl }, async () => {
297
+ * const channels = await communicationChannel.list({ filter: { identifierValue: phoneNumber } })
298
+ * // ...
299
+ * })
300
+ * ```
301
+ */
302
+ async exchange(appInstallationId) {
303
+ const payload = (await callCore('token.exchange', {
304
+ appInstallationId,
305
+ }));
306
+ return payload;
307
+ },
308
+ };
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export * from './types';
3
3
  export * from './schemas';
4
4
  export { server } from './server';
5
5
  export { z };
6
- export { workplace, communicationChannel, instance, configure, getConfig, runWithConfig, } from './core/client';
6
+ export { workplace, communicationChannel, instance, token, configure, getConfig, runWithConfig, } from './core/client';
7
7
  export type { InstanceContext, InstanceData, InstanceMeta, InstancePagination, InstanceListResult, InstanceListArgs, } from './core/client';
8
8
  declare const _default: {
9
9
  z: typeof z;
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.CONFIG_FILE_NAMES = exports.getAllEnvKeys = exports.getRequiredInstallEnvKeys = exports.validateConfig = exports.loadConfig = exports.defineConfig = exports.runWithConfig = exports.getConfig = exports.configure = exports.instance = exports.communicationChannel = exports.workplace = exports.z = exports.server = void 0;
17
+ exports.CONFIG_FILE_NAMES = exports.getAllEnvKeys = exports.getRequiredInstallEnvKeys = exports.validateConfig = exports.loadConfig = exports.defineConfig = exports.runWithConfig = exports.getConfig = exports.configure = exports.token = exports.instance = exports.communicationChannel = exports.workplace = exports.z = exports.server = void 0;
18
18
  const zod_1 = require("zod");
19
19
  Object.defineProperty(exports, "z", { enumerable: true, get: function () { return zod_1.z; } });
20
20
  __exportStar(require("./types"), exports);
@@ -25,6 +25,7 @@ var client_1 = require("./core/client");
25
25
  Object.defineProperty(exports, "workplace", { enumerable: true, get: function () { return client_1.workplace; } });
26
26
  Object.defineProperty(exports, "communicationChannel", { enumerable: true, get: function () { return client_1.communicationChannel; } });
27
27
  Object.defineProperty(exports, "instance", { enumerable: true, get: function () { return client_1.instance; } });
28
+ Object.defineProperty(exports, "token", { enumerable: true, get: function () { return client_1.token; } });
28
29
  Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return client_1.configure; } });
29
30
  Object.defineProperty(exports, "getConfig", { enumerable: true, get: function () { return client_1.getConfig; } });
30
31
  Object.defineProperty(exports, "runWithConfig", { enumerable: true, get: function () { return client_1.runWithConfig; } });
package/dist/schemas.d.ts CHANGED
@@ -464,14 +464,20 @@ export declare const PageFieldTypeSchema: z.ZodEnum<{
464
464
  DATE: "DATE";
465
465
  FILE: "FILE";
466
466
  SELECT: "SELECT";
467
+ FORM: "FORM";
467
468
  }>;
468
469
  /** Data source for prepopulating a page field from a model */
469
470
  export declare const PageFieldSourceSchema: z.ZodObject<{
470
471
  model: z.ZodString;
471
472
  field: z.ZodString;
472
473
  }, z.core.$strip>;
473
- /** Self-contained field definition for page blocks */
474
- export declare const PageFieldDefinitionSchema: z.ZodObject<{
474
+ /** Header configuration for FORM type fields */
475
+ export declare const PageFormHeaderSchema: z.ZodObject<{
476
+ title: z.ZodString;
477
+ description: z.ZodOptional<z.ZodString>;
478
+ }, z.core.$strip>;
479
+ /** Base field definition (without nested fields to avoid circular reference) */
480
+ declare const PageFieldDefinitionBaseSchema: z.ZodObject<{
475
481
  handle: z.ZodString;
476
482
  type: z.ZodEnum<{
477
483
  STRING: "STRING";
@@ -480,6 +486,7 @@ export declare const PageFieldDefinitionSchema: z.ZodObject<{
480
486
  DATE: "DATE";
481
487
  FILE: "FILE";
482
488
  SELECT: "SELECT";
489
+ FORM: "FORM";
483
490
  }>;
484
491
  label: z.ZodString;
485
492
  description: z.ZodOptional<z.ZodString>;
@@ -495,6 +502,14 @@ export declare const PageFieldDefinitionSchema: z.ZodObject<{
495
502
  }, z.core.$strip>>>;
496
503
  accept: z.ZodOptional<z.ZodString>;
497
504
  }, z.core.$strip>;
505
+ /** Self-contained field definition for page blocks */
506
+ export declare const PageFieldDefinitionSchema: z.ZodType<PageFieldDefinition>;
507
+ /** Page field definition type */
508
+ export type PageFieldDefinition = z.infer<typeof PageFieldDefinitionBaseSchema> & {
509
+ header?: z.infer<typeof PageFormHeaderSchema>;
510
+ fields?: PageFieldDefinition[];
511
+ actions?: PageActionDefinition[];
512
+ };
498
513
  /** Action button definition for pages */
499
514
  export declare const PageActionDefinitionSchema: z.ZodObject<{
500
515
  handle: z.ZodString;
@@ -520,30 +535,7 @@ export declare const PageBlockDefinitionSchema: z.ZodObject<{
520
535
  link: "link";
521
536
  }>;
522
537
  title: z.ZodOptional<z.ZodString>;
523
- fields: z.ZodOptional<z.ZodArray<z.ZodObject<{
524
- handle: z.ZodString;
525
- type: z.ZodEnum<{
526
- STRING: "STRING";
527
- NUMBER: "NUMBER";
528
- BOOLEAN: "BOOLEAN";
529
- DATE: "DATE";
530
- FILE: "FILE";
531
- SELECT: "SELECT";
532
- }>;
533
- label: z.ZodString;
534
- description: z.ZodOptional<z.ZodString>;
535
- required: z.ZodOptional<z.ZodBoolean>;
536
- handler: z.ZodOptional<z.ZodString>;
537
- source: z.ZodOptional<z.ZodObject<{
538
- model: z.ZodString;
539
- field: z.ZodString;
540
- }, z.core.$strip>>;
541
- options: z.ZodOptional<z.ZodArray<z.ZodObject<{
542
- value: z.ZodString;
543
- label: z.ZodString;
544
- }, z.core.$strip>>>;
545
- accept: z.ZodOptional<z.ZodString>;
546
- }, z.core.$strip>>>;
538
+ fields: z.ZodOptional<z.ZodArray<z.ZodType<PageFieldDefinition, unknown, z.core.$ZodTypeInternals<PageFieldDefinition, unknown>>>>;
547
539
  readonly: z.ZodOptional<z.ZodBoolean>;
548
540
  }, z.core.$strip>;
549
541
  /** Filter for selecting which instance(s) to display on a page */
@@ -573,30 +565,7 @@ export declare const PageDefinitionSchema: z.ZodObject<{
573
565
  link: "link";
574
566
  }>;
575
567
  title: z.ZodOptional<z.ZodString>;
576
- fields: z.ZodOptional<z.ZodArray<z.ZodObject<{
577
- handle: z.ZodString;
578
- type: z.ZodEnum<{
579
- STRING: "STRING";
580
- NUMBER: "NUMBER";
581
- BOOLEAN: "BOOLEAN";
582
- DATE: "DATE";
583
- FILE: "FILE";
584
- SELECT: "SELECT";
585
- }>;
586
- label: z.ZodString;
587
- description: z.ZodOptional<z.ZodString>;
588
- required: z.ZodOptional<z.ZodBoolean>;
589
- handler: z.ZodOptional<z.ZodString>;
590
- source: z.ZodOptional<z.ZodObject<{
591
- model: z.ZodString;
592
- field: z.ZodString;
593
- }, z.core.$strip>>;
594
- options: z.ZodOptional<z.ZodArray<z.ZodObject<{
595
- value: z.ZodString;
596
- label: z.ZodString;
597
- }, z.core.$strip>>>;
598
- accept: z.ZodOptional<z.ZodString>;
599
- }, z.core.$strip>>>;
568
+ fields: z.ZodOptional<z.ZodArray<z.ZodType<PageFieldDefinition, unknown, z.core.$ZodTypeInternals<PageFieldDefinition, unknown>>>>;
600
569
  readonly: z.ZodOptional<z.ZodBoolean>;
601
570
  }, z.core.$strip>>;
602
571
  actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -1034,30 +1003,7 @@ export declare const SkedyulConfigSchema: z.ZodObject<{
1034
1003
  link: "link";
1035
1004
  }>;
1036
1005
  title: z.ZodOptional<z.ZodString>;
1037
- fields: z.ZodOptional<z.ZodArray<z.ZodObject<{
1038
- handle: z.ZodString;
1039
- type: z.ZodEnum<{
1040
- STRING: "STRING";
1041
- NUMBER: "NUMBER";
1042
- BOOLEAN: "BOOLEAN";
1043
- DATE: "DATE";
1044
- FILE: "FILE";
1045
- SELECT: "SELECT";
1046
- }>;
1047
- label: z.ZodString;
1048
- description: z.ZodOptional<z.ZodString>;
1049
- required: z.ZodOptional<z.ZodBoolean>;
1050
- handler: z.ZodOptional<z.ZodString>;
1051
- source: z.ZodOptional<z.ZodObject<{
1052
- model: z.ZodString;
1053
- field: z.ZodString;
1054
- }, z.core.$strip>>;
1055
- options: z.ZodOptional<z.ZodArray<z.ZodObject<{
1056
- value: z.ZodString;
1057
- label: z.ZodString;
1058
- }, z.core.$strip>>>;
1059
- accept: z.ZodOptional<z.ZodString>;
1060
- }, z.core.$strip>>>;
1006
+ fields: z.ZodOptional<z.ZodArray<z.ZodType<PageFieldDefinition, unknown, z.core.$ZodTypeInternals<PageFieldDefinition, unknown>>>>;
1061
1007
  readonly: z.ZodOptional<z.ZodBoolean>;
1062
1008
  }, z.core.$strip>>;
1063
1009
  actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -1292,3 +1238,4 @@ export declare function isModelDependency(dep: ResourceDependency): dep is Model
1292
1238
  export declare function isChannelDependency(dep: ResourceDependency): dep is ChannelDependency;
1293
1239
  /** Check if a dependency is a workflow dependency */
1294
1240
  export declare function isWorkflowDependency(dep: ResourceDependency): dep is WorkflowDependency;
1241
+ export {};
package/dist/schemas.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SkedyulConfigSchema = exports.InternalModelDefinitionSchema = exports.InternalFieldDefinitionSchema = exports.InternalFieldDataTypeSchema = exports.ModelDefinitionSchema = exports.ModelFieldDefinitionSchema = exports.InlineFieldDefinitionSchema = exports.PageDefinitionSchema = exports.PageInstanceFilterSchema = exports.PageBlockDefinitionSchema = exports.PageActionDefinitionSchema = exports.PageFieldDefinitionSchema = exports.PageFieldSourceSchema = exports.PageFieldTypeSchema = exports.PageBlockTypeSchema = exports.PageTypeSchema = exports.RelationshipDefinitionSchema = exports.RelationshipLinkSchema = exports.OnDeleteBehaviorSchema = exports.RelationshipCardinalitySchema = exports.FieldOptionSchema = exports.FieldDataTypeSchema = exports.ComputeLayerTypeSchema = exports.WorkflowDefinitionSchema = exports.WorkflowActionSchema = exports.WorkflowActionInputSchema = exports.ChannelDefinitionSchema = exports.CommunicationChannelDefinitionSchema = exports.ResourceDependencySchema = exports.WorkflowDependencySchema = exports.ChannelDependencySchema = exports.ModelDependencySchema = exports.StructuredFilterSchema = exports.FieldOwnerSchema = exports.ResourceScopeSchema = exports.ChannelIdentifierValueSchema = exports.ChannelIdentifierTypeSchema = exports.ChannelToolBindingsSchema = exports.AppFieldDefinitionSchema = exports.AppFieldVisibilitySchema = exports.InstallConfigSchema = exports.AppModelDefinitionSchema = exports.EnvSchemaSchema = exports.EnvVariableDefinitionSchema = exports.EnvVisibilitySchema = void 0;
3
+ exports.SkedyulConfigSchema = exports.InternalModelDefinitionSchema = exports.InternalFieldDefinitionSchema = exports.InternalFieldDataTypeSchema = exports.ModelDefinitionSchema = exports.ModelFieldDefinitionSchema = exports.InlineFieldDefinitionSchema = exports.PageDefinitionSchema = exports.PageInstanceFilterSchema = exports.PageBlockDefinitionSchema = exports.PageActionDefinitionSchema = exports.PageFieldDefinitionSchema = exports.PageFormHeaderSchema = exports.PageFieldSourceSchema = exports.PageFieldTypeSchema = exports.PageBlockTypeSchema = exports.PageTypeSchema = exports.RelationshipDefinitionSchema = exports.RelationshipLinkSchema = exports.OnDeleteBehaviorSchema = exports.RelationshipCardinalitySchema = exports.FieldOptionSchema = exports.FieldDataTypeSchema = exports.ComputeLayerTypeSchema = exports.WorkflowDefinitionSchema = exports.WorkflowActionSchema = exports.WorkflowActionInputSchema = exports.ChannelDefinitionSchema = exports.CommunicationChannelDefinitionSchema = exports.ResourceDependencySchema = exports.WorkflowDependencySchema = exports.ChannelDependencySchema = exports.ModelDependencySchema = exports.StructuredFilterSchema = exports.FieldOwnerSchema = exports.ResourceScopeSchema = exports.ChannelIdentifierValueSchema = exports.ChannelIdentifierTypeSchema = exports.ChannelToolBindingsSchema = exports.AppFieldDefinitionSchema = exports.AppFieldVisibilitySchema = exports.InstallConfigSchema = exports.AppModelDefinitionSchema = exports.EnvSchemaSchema = exports.EnvVariableDefinitionSchema = exports.EnvVisibilitySchema = void 0;
4
4
  exports.safeParseConfig = safeParseConfig;
5
5
  exports.isModelDependency = isModelDependency;
6
6
  exports.isChannelDependency = isChannelDependency;
@@ -316,6 +316,7 @@ exports.PageFieldTypeSchema = zod_1.z.enum([
316
316
  'DATE',
317
317
  'BOOLEAN',
318
318
  'SELECT',
319
+ 'FORM', // Nested form that opens in a modal
319
320
  ]);
320
321
  /** Data source for prepopulating a page field from a model */
321
322
  exports.PageFieldSourceSchema = zod_1.z.object({
@@ -324,8 +325,15 @@ exports.PageFieldSourceSchema = zod_1.z.object({
324
325
  /** Field handle on that model */
325
326
  field: zod_1.z.string(),
326
327
  });
327
- /** Self-contained field definition for page blocks */
328
- exports.PageFieldDefinitionSchema = zod_1.z.object({
328
+ /** Header configuration for FORM type fields */
329
+ exports.PageFormHeaderSchema = zod_1.z.object({
330
+ /** Modal title */
331
+ title: zod_1.z.string(),
332
+ /** Optional description shown below title */
333
+ description: zod_1.z.string().optional(),
334
+ });
335
+ /** Base field definition (without nested fields to avoid circular reference) */
336
+ const PageFieldDefinitionBaseSchema = zod_1.z.object({
329
337
  /** Unique handle for this field */
330
338
  handle: zod_1.z.string(),
331
339
  /** Field datatype - determines UI component */
@@ -336,7 +344,7 @@ exports.PageFieldDefinitionSchema = zod_1.z.object({
336
344
  description: zod_1.z.string().optional(),
337
345
  /** Whether field is required */
338
346
  required: zod_1.z.boolean().optional(),
339
- /** Tool name from registry to call on value change */
347
+ /** Tool name from registry to call on value change or form submit */
340
348
  handler: zod_1.z.string().optional(),
341
349
  /** Data source for prepopulating field value */
342
350
  source: exports.PageFieldSourceSchema.optional(),
@@ -345,6 +353,15 @@ exports.PageFieldDefinitionSchema = zod_1.z.object({
345
353
  /** For FILE type: accepted file extensions */
346
354
  accept: zod_1.z.string().optional(),
347
355
  });
356
+ /** Self-contained field definition for page blocks */
357
+ exports.PageFieldDefinitionSchema = PageFieldDefinitionBaseSchema.extend({
358
+ /** For FORM type: header configuration for the modal */
359
+ header: exports.PageFormHeaderSchema.optional(),
360
+ /** For FORM type: nested fields rendered inside the modal form */
361
+ fields: zod_1.z.lazy(() => zod_1.z.array(exports.PageFieldDefinitionSchema)).optional(),
362
+ /** For FORM type: action buttons in the modal footer */
363
+ actions: zod_1.z.lazy(() => zod_1.z.array(exports.PageActionDefinitionSchema)).optional(),
364
+ });
348
365
  /** Action button definition for pages */
349
366
  exports.PageActionDefinitionSchema = zod_1.z.object({
350
367
  /** Unique handle for this action */
package/dist/types.d.ts CHANGED
@@ -57,7 +57,7 @@ export interface ToolParams<Input, Output> {
57
57
  context: ToolContext;
58
58
  }
59
59
  /** Trigger types for tool execution */
60
- export type ToolTrigger = 'field_change' | 'page_action' | 'agent' | 'workflow';
60
+ export type ToolTrigger = 'field_change' | 'page_action' | 'agent' | 'workflow' | 'form_submit';
61
61
  /** Field info when tool is triggered by a field change */
62
62
  export interface ToolFieldContext {
63
63
  /** Field handle from page definition */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skedyul",
3
- "version": "0.1.56",
3
+ "version": "0.1.58",
4
4
  "description": "The Skedyul SDK for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",