skedyul 1.7.6 → 1.7.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -1988,6 +1988,128 @@ async function handleMcpBatchRoute(req, ctx) {
1988
1988
  }
1989
1989
  };
1990
1990
  }
1991
+ async function handleBatchOperationRoute(req, ctx) {
1992
+ const bodyResult = parseJsonBody(req);
1993
+ if (!bodyResult.success) {
1994
+ return bodyResult.error;
1995
+ }
1996
+ const body = bodyResult.data;
1997
+ const registry = ctx.batchOperationRegistry;
1998
+ if (!registry) {
1999
+ return {
2000
+ status: 404,
2001
+ body: {
2002
+ success: false,
2003
+ error: "No batch operations registered"
2004
+ }
2005
+ };
2006
+ }
2007
+ const operation = registry[body.handle];
2008
+ if (!operation) {
2009
+ return {
2010
+ status: 404,
2011
+ body: {
2012
+ success: false,
2013
+ error: `Batch operation not found: ${body.handle}`
2014
+ }
2015
+ };
2016
+ }
2017
+ const context = body.context;
2018
+ if (!context?.workplaceId || !context?.appInstallationId || !context?.appId) {
2019
+ return {
2020
+ status: 400,
2021
+ body: {
2022
+ success: false,
2023
+ error: "Missing context (workplaceId, appInstallationId, and appId required)"
2024
+ }
2025
+ };
2026
+ }
2027
+ const env = body.env ?? {};
2028
+ const log = {
2029
+ info: (message, meta) => {
2030
+ console.log(`[batch:${body.handle}] ${message}`, meta ?? "");
2031
+ },
2032
+ warn: (message, meta) => {
2033
+ console.warn(`[batch:${body.handle}] ${message}`, meta ?? "");
2034
+ },
2035
+ error: (message, meta) => {
2036
+ console.error(`[batch:${body.handle}] ${message}`, meta ?? "");
2037
+ }
2038
+ };
2039
+ const requestConfig = {
2040
+ baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2041
+ apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2042
+ };
2043
+ try {
2044
+ if (body.method === "setup") {
2045
+ if (!operation.setup) {
2046
+ return {
2047
+ status: 200,
2048
+ body: {
2049
+ success: true,
2050
+ result: { state: {}, total: void 0 }
2051
+ }
2052
+ };
2053
+ }
2054
+ const result = await runWithConfig(requestConfig, async () => {
2055
+ return operation.setup({
2056
+ workplaceId: context.workplaceId,
2057
+ appInstallationId: context.appInstallationId,
2058
+ appId: context.appId,
2059
+ input: body.input,
2060
+ env,
2061
+ log
2062
+ });
2063
+ });
2064
+ return {
2065
+ status: 200,
2066
+ body: {
2067
+ success: true,
2068
+ result
2069
+ }
2070
+ };
2071
+ }
2072
+ if (body.method === "iterate") {
2073
+ const result = await runWithConfig(requestConfig, async () => {
2074
+ return operation.iterate({
2075
+ workplaceId: context.workplaceId,
2076
+ appInstallationId: context.appInstallationId,
2077
+ appId: context.appId,
2078
+ input: body.input,
2079
+ state: body.state,
2080
+ page: body.page,
2081
+ cursor: body.cursor,
2082
+ limit: body.limit,
2083
+ env,
2084
+ log
2085
+ });
2086
+ });
2087
+ return {
2088
+ status: 200,
2089
+ body: {
2090
+ success: true,
2091
+ result
2092
+ }
2093
+ };
2094
+ }
2095
+ return {
2096
+ status: 400,
2097
+ body: {
2098
+ success: false,
2099
+ error: `Unknown method: ${body.method}`
2100
+ }
2101
+ };
2102
+ } catch (err) {
2103
+ log.error("Batch operation failed", { error: err });
2104
+ return {
2105
+ status: 500,
2106
+ body: {
2107
+ success: false,
2108
+ error: err instanceof Error ? err.message : String(err)
2109
+ }
2110
+ };
2111
+ }
2112
+ }
1991
2113
  function createNotFoundResponse() {
1992
2114
  return {
1993
2115
  status: 404,
@@ -2067,6 +2189,9 @@ async function routeRequest(req, ctx) {
2067
2189
  if (req.path === "/mcp/batch" && req.method === "POST") {
2068
2190
  return handleMcpBatchRoute(req, ctx);
2069
2191
  }
2192
+ if (req.path === "/batch-operation" && req.method === "POST") {
2193
+ return handleBatchOperationRoute(req, ctx);
2194
+ }
2070
2195
  return createNotFoundResponse();
2071
2196
  } catch (err) {
2072
2197
  return createErrorResponse(err);
@@ -2108,11 +2233,13 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2108
2233
  const defaultHeaders = getDefaultHeaders(config.cors);
2109
2234
  const registry = config.tools;
2110
2235
  const webhookRegistry = config.webhooks;
2236
+ const batchOperationRegistry = config.batchOperations;
2111
2237
  const ctx = {
2112
2238
  config,
2113
2239
  tools,
2114
2240
  registry,
2115
2241
  webhookRegistry,
2242
+ batchOperationRegistry,
2116
2243
  callTool,
2117
2244
  state,
2118
2245
  mcpServer,
@@ -2227,12 +2354,14 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
2227
2354
  const defaultHeaders = getDefaultHeaders(config.cors);
2228
2355
  const registry = config.tools;
2229
2356
  const webhookRegistry = config.webhooks;
2357
+ const batchOperationRegistry = config.batchOperations;
2230
2358
  let hasLoggedStartup = false;
2231
2359
  const ctx = {
2232
2360
  config,
2233
2361
  tools,
2234
2362
  registry,
2235
2363
  webhookRegistry,
2364
+ batchOperationRegistry,
2236
2365
  callTool,
2237
2366
  state,
2238
2367
  mcpServer,
@@ -1949,6 +1949,128 @@ async function handleMcpBatchRoute(req, ctx) {
1949
1949
  }
1950
1950
  };
1951
1951
  }
1952
+ async function handleBatchOperationRoute(req, ctx) {
1953
+ const bodyResult = parseJsonBody(req);
1954
+ if (!bodyResult.success) {
1955
+ return bodyResult.error;
1956
+ }
1957
+ const body = bodyResult.data;
1958
+ const registry = ctx.batchOperationRegistry;
1959
+ if (!registry) {
1960
+ return {
1961
+ status: 404,
1962
+ body: {
1963
+ success: false,
1964
+ error: "No batch operations registered"
1965
+ }
1966
+ };
1967
+ }
1968
+ const operation = registry[body.handle];
1969
+ if (!operation) {
1970
+ return {
1971
+ status: 404,
1972
+ body: {
1973
+ success: false,
1974
+ error: `Batch operation not found: ${body.handle}`
1975
+ }
1976
+ };
1977
+ }
1978
+ const context = body.context;
1979
+ if (!context?.workplaceId || !context?.appInstallationId || !context?.appId) {
1980
+ return {
1981
+ status: 400,
1982
+ body: {
1983
+ success: false,
1984
+ error: "Missing context (workplaceId, appInstallationId, and appId required)"
1985
+ }
1986
+ };
1987
+ }
1988
+ const env = body.env ?? {};
1989
+ const log = {
1990
+ info: (message, meta) => {
1991
+ console.log(`[batch:${body.handle}] ${message}`, meta ?? "");
1992
+ },
1993
+ warn: (message, meta) => {
1994
+ console.warn(`[batch:${body.handle}] ${message}`, meta ?? "");
1995
+ },
1996
+ error: (message, meta) => {
1997
+ console.error(`[batch:${body.handle}] ${message}`, meta ?? "");
1998
+ }
1999
+ };
2000
+ const requestConfig = {
2001
+ baseUrl: env.SKEDYUL_API_URL ?? process.env.SKEDYUL_API_URL ?? "",
2002
+ apiToken: env.SKEDYUL_API_TOKEN ?? process.env.SKEDYUL_API_TOKEN ?? ""
2003
+ };
2004
+ try {
2005
+ if (body.method === "setup") {
2006
+ if (!operation.setup) {
2007
+ return {
2008
+ status: 200,
2009
+ body: {
2010
+ success: true,
2011
+ result: { state: {}, total: void 0 }
2012
+ }
2013
+ };
2014
+ }
2015
+ const result = await runWithConfig(requestConfig, async () => {
2016
+ return operation.setup({
2017
+ workplaceId: context.workplaceId,
2018
+ appInstallationId: context.appInstallationId,
2019
+ appId: context.appId,
2020
+ input: body.input,
2021
+ env,
2022
+ log
2023
+ });
2024
+ });
2025
+ return {
2026
+ status: 200,
2027
+ body: {
2028
+ success: true,
2029
+ result
2030
+ }
2031
+ };
2032
+ }
2033
+ if (body.method === "iterate") {
2034
+ const result = await runWithConfig(requestConfig, async () => {
2035
+ return operation.iterate({
2036
+ workplaceId: context.workplaceId,
2037
+ appInstallationId: context.appInstallationId,
2038
+ appId: context.appId,
2039
+ input: body.input,
2040
+ state: body.state,
2041
+ page: body.page,
2042
+ cursor: body.cursor,
2043
+ limit: body.limit,
2044
+ env,
2045
+ log
2046
+ });
2047
+ });
2048
+ return {
2049
+ status: 200,
2050
+ body: {
2051
+ success: true,
2052
+ result
2053
+ }
2054
+ };
2055
+ }
2056
+ return {
2057
+ status: 400,
2058
+ body: {
2059
+ success: false,
2060
+ error: `Unknown method: ${body.method}`
2061
+ }
2062
+ };
2063
+ } catch (err) {
2064
+ log.error("Batch operation failed", { error: err });
2065
+ return {
2066
+ status: 500,
2067
+ body: {
2068
+ success: false,
2069
+ error: err instanceof Error ? err.message : String(err)
2070
+ }
2071
+ };
2072
+ }
2073
+ }
1952
2074
  function createNotFoundResponse() {
1953
2075
  return {
1954
2076
  status: 404,
@@ -2028,6 +2150,9 @@ async function routeRequest(req, ctx) {
2028
2150
  if (req.path === "/mcp/batch" && req.method === "POST") {
2029
2151
  return handleMcpBatchRoute(req, ctx);
2030
2152
  }
2153
+ if (req.path === "/batch-operation" && req.method === "POST") {
2154
+ return handleBatchOperationRoute(req, ctx);
2155
+ }
2031
2156
  return createNotFoundResponse();
2032
2157
  } catch (err) {
2033
2158
  return createErrorResponse(err);
@@ -2069,11 +2194,13 @@ function createDedicatedServerInstance(config, tools, callTool, state, mcpServer
2069
2194
  const defaultHeaders = getDefaultHeaders(config.cors);
2070
2195
  const registry = config.tools;
2071
2196
  const webhookRegistry = config.webhooks;
2197
+ const batchOperationRegistry = config.batchOperations;
2072
2198
  const ctx = {
2073
2199
  config,
2074
2200
  tools,
2075
2201
  registry,
2076
2202
  webhookRegistry,
2203
+ batchOperationRegistry,
2077
2204
  callTool,
2078
2205
  state,
2079
2206
  mcpServer,
@@ -2188,12 +2315,14 @@ function createServerlessInstance(config, tools, callTool, state, mcpServer) {
2188
2315
  const defaultHeaders = getDefaultHeaders(config.cors);
2189
2316
  const registry = config.tools;
2190
2317
  const webhookRegistry = config.webhooks;
2318
+ const batchOperationRegistry = config.batchOperations;
2191
2319
  let hasLoggedStartup = false;
2192
2320
  const ctx = {
2193
2321
  config,
2194
2322
  tools,
2195
2323
  registry,
2196
2324
  webhookRegistry,
2325
+ batchOperationRegistry,
2197
2326
  callTool,
2198
2327
  state,
2199
2328
  mcpServer,
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Batch operation runtime types.
3
+ *
4
+ * These types define the registry and context for batch operations at runtime.
5
+ */
6
+ import type { BatchOperationSetupFn, BatchOperationIterateFn } from '../config/types/batch-operation';
7
+ export type { BatchOperationContext } from '../config/types/batch-operation';
8
+ /**
9
+ * Entry in the batch operation registry.
10
+ * Contains both metadata and function implementations.
11
+ */
12
+ export interface BatchOperationRegistryEntry {
13
+ /** Unique handle for this operation */
14
+ handle: string;
15
+ /** Human-readable label */
16
+ label: string;
17
+ /** Description of what this operation does */
18
+ description?: string;
19
+ /** Entity handle that items will be upserted to */
20
+ entity: string;
21
+ /** Setup function called once at the start */
22
+ setup?: BatchOperationSetupFn;
23
+ /** Iterate function called for each page */
24
+ iterate: BatchOperationIterateFn;
25
+ /** Maximum concurrent operations per app installation */
26
+ maxConcurrent?: number;
27
+ /** Default page size */
28
+ pageSize?: number;
29
+ /** Optional icon */
30
+ icon?: string;
31
+ }
32
+ /**
33
+ * Registry mapping batch operation handles to their implementations.
34
+ */
35
+ export type BatchOperationRegistry = Record<string, BatchOperationRegistryEntry>;
36
+ /**
37
+ * Metadata for a batch operation (serializable, no functions).
38
+ */
39
+ export interface BatchOperationMetadata {
40
+ handle: string;
41
+ label: string;
42
+ description?: string;
43
+ entity: string;
44
+ maxConcurrent?: number;
45
+ pageSize?: number;
46
+ icon?: string;
47
+ }
@@ -22,3 +22,4 @@ export type { WebhookWireRequest, HandlerRawRequest, WebhookRequest, WebhookResp
22
22
  export { isRuntimeWebhookContext } from './webhook';
23
23
  export type { MCPError, MCPContentItem, MCPResult, MCPResponse, MCPRequest, } from './mcp-protocol';
24
24
  export type { DataBlock, DataBlockLink, SpreadsheetBlock, SpreadsheetColumn, ProfileBlock, ProfileAvatar, ProfileField, FieldChangesBlock, FieldChangeItem, DateTimeBlock, } from './data-blocks';
25
+ export type { BatchOperationContext, BatchOperationRegistryEntry, BatchOperationRegistry, BatchOperationMetadata, } from './batch-operation';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skedyul",
3
- "version": "1.7.6",
3
+ "version": "1.7.8",
4
4
  "description": "The Skedyul SDK for Node.js",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",