veryfront 0.1.1063 → 0.1.1064

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.
@@ -1 +1 @@
1
- {"version":3,"file":"command-help.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/pull/command-help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,eAAO,MAAM,QAAQ,EAAE,WAuEtB,CAAC"}
1
+ {"version":3,"file":"command-help.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/pull/command-help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,eAAO,MAAM,QAAQ,EAAE,WA4EtB,CAAC"}
@@ -66,7 +66,12 @@ export const pullHelp = {
66
66
  "With --projects, each project is pulled into a subdirectory named after the slug",
67
67
  'Projects list can also be specified in veryfront.json: { "projects": ["slug1", "slug2"] }',
68
68
  "Priority order: --env > --release > --branch > main",
69
- "--prune requires a clean Git worktree; --prune --dry-run can preview anywhere",
70
- "--prune never removes ignored or unsupported files",
69
+ "--prune applies a full managed-source snapshot; it does not perform a Git merge",
70
+ "Mutating --prune requires a clean Git worktree; --yes and --force skip confirmation only",
71
+ "--prune --dry-run can preview anywhere and never writes or deletes local files",
72
+ "Only supported text files are managed; ignored, unsupported, and binary files remain unchanged",
73
+ "Pull preserves remote bytes exactly, including line endings and a missing final newline",
74
+ "Invalid, duplicate, reserved, or symlink-traversing remote paths fail before local writes",
75
+ "A fetch failure causes no writes or pruning; a local write or delete failure exits with an error",
71
76
  ],
72
77
  };
@@ -1 +1 @@
1
- {"version":3,"file":"command-help.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/push/command-help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,eAAO,MAAM,QAAQ,EAAE,WA+CtB,CAAC"}
1
+ {"version":3,"file":"command-help.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/push/command-help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,eAAO,MAAM,QAAQ,EAAE,WAiDtB,CAAC"}
@@ -44,5 +44,7 @@ export const pushHelp = {
44
44
  "Use --branch main to update the existing Veryfront main branch",
45
45
  "Uploads supported text source files using their relative paths",
46
46
  ".vfignore controls which supported files are excluded",
47
+ "--dry-run never creates a project or branch, changes remote files, or writes a Push receipt",
48
+ "If an upload fails, Push does not delete remote files",
47
49
  ],
48
50
  };
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/push/command.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/push/command.ts"],"names":[],"mappings":"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"}
@@ -305,38 +305,47 @@ export function pushCommand(options = {}) {
305
305
  const client = createApiClient(config);
306
306
  const branchName = branch || generateBranchName();
307
307
  const isMainBranch = branchName === "main";
308
- // First-push: If project doesn't exist on server yet, create it
308
+ // First-push: If project doesn't exist on server yet, create it unless this is a dry run.
309
309
  let mainFiles = [];
310
+ let projectExists = true;
310
311
  try {
311
312
  mainFiles = await listAllFiles(client, config.projectSlug, { type: "main" });
312
313
  }
313
314
  catch (error) {
314
315
  // Project doesn't exist yet - create it on first push
315
316
  if (getErrorStatus(error) === 404) {
316
- spinner.update("Creating project...");
317
- let reserveResult;
318
- try {
319
- reserveResult = await reserveProjectSlug(config.projectSlug, config.apiToken, undefined, config.apiUrl, { allowAlternativeSlug: canPersistAlternativeSlug(projectReferenceSource) });
320
- }
321
- catch (reserveError) {
322
- spinner.stop();
323
- if (reserveError instanceof ProjectSlugConflictError) {
324
- throw projectSlugConflictError(reserveError, projectReferenceSource);
317
+ if (dryRun) {
318
+ projectExists = false;
319
+ if (!quiet) {
320
+ logInfo(`Project "${config.projectSlug}" does not exist. Dry run will not create it.`);
325
321
  }
326
- throw reserveError;
327
- }
328
- if (reserveResult.slug !== config.projectSlug) {
329
- await writeProjectSlug(projectDir, reserveResult.slug);
330
- logInfo(`Project slug: ${reserveResult.slug}`);
331
322
  }
332
- config = { ...config, projectSlug: reserveResult.slug };
333
- // Now try to get files again (should be empty for new project)
334
- try {
335
- mainFiles = await listAllFiles(client, config.projectSlug, { type: "main" });
336
- }
337
- catch {
338
- // Project just created, no files yet
339
- mainFiles = [];
323
+ else {
324
+ spinner.update("Creating project...");
325
+ let reserveResult;
326
+ try {
327
+ reserveResult = await reserveProjectSlug(config.projectSlug, config.apiToken, undefined, config.apiUrl, { allowAlternativeSlug: canPersistAlternativeSlug(projectReferenceSource) });
328
+ }
329
+ catch (reserveError) {
330
+ spinner.stop();
331
+ if (reserveError instanceof ProjectSlugConflictError) {
332
+ throw projectSlugConflictError(reserveError, projectReferenceSource);
333
+ }
334
+ throw reserveError;
335
+ }
336
+ if (reserveResult.slug !== config.projectSlug) {
337
+ await writeProjectSlug(projectDir, reserveResult.slug);
338
+ logInfo(`Project slug: ${reserveResult.slug}`);
339
+ }
340
+ config = { ...config, projectSlug: reserveResult.slug };
341
+ // Now try to get files again (should be empty for new project)
342
+ try {
343
+ mainFiles = await listAllFiles(client, config.projectSlug, { type: "main" });
344
+ }
345
+ catch {
346
+ // Project just created, no files yet
347
+ mainFiles = [];
348
+ }
340
349
  }
341
350
  }
342
351
  else {
@@ -354,7 +363,13 @@ export function pushCommand(options = {}) {
354
363
  }
355
364
  const ops = sourceSnapshot.files;
356
365
  const localPaths = new Set(ops.map((op) => op.path));
357
- const target = await resolvePushRemoteFiles(client, config.projectSlug, branchName, mainFiles);
366
+ const target = projectExists
367
+ ? await resolvePushRemoteFiles(client, config.projectSlug, branchName, mainFiles)
368
+ : {
369
+ branchId: null,
370
+ remoteFiles: mainFiles,
371
+ source: { type: "main" },
372
+ };
358
373
  const toDelete = target.remoteFiles.map((f) => f.path).filter((p) => !localPaths.has(p));
359
374
  if (ops.length === 0 && toDelete.length === 0) {
360
375
  try {
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.1063",
3
+ "version": "0.1.1064",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "minimumDependencyAge": {
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/src/schedule/factory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAA6B,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAkBhG,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,kBAAkB,CAsCnE"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/src/schedule/factory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,cAAc,EACd,kBAAkB,EAInB,MAAM,YAAY,CAAC;AA8LpB,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,kBAAkB,CAwCnE"}
@@ -1,6 +1,8 @@
1
1
  import { isTriggerTarget } from "../trigger/target.js";
2
2
  import { assertSerializable, validateTriggerId } from "../trigger/validation.js";
3
3
  const CONCURRENCY_POLICIES = new Set(["Allow", "Forbid", "Replace"]);
4
+ const INTEGRATION_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]*$/;
5
+ const REQUIREMENT_KIND_PATTERN = /^[a-z][a-z0-9_-]*$/;
4
6
  function requireString(value, label) {
5
7
  if (typeof value !== "string" || value.trim().length === 0) {
6
8
  throw new Error(`${label} is required.`);
@@ -14,6 +16,107 @@ function validatePositiveInteger(value, label) {
14
16
  throw new Error(`${label} must be a positive integer.`);
15
17
  }
16
18
  }
19
+ function requireContractString(value, label, maxLength) {
20
+ const normalized = requireString(value, label).trim();
21
+ if (normalized.length > maxLength) {
22
+ throw new Error(`${label} must be at most ${maxLength} characters.`);
23
+ }
24
+ return normalized;
25
+ }
26
+ function requireIntegrationName(value, label) {
27
+ const normalized = requireContractString(value, label, 255);
28
+ if (!INTEGRATION_NAME_PATTERN.test(normalized)) {
29
+ throw new Error(`${label} must use a lowercase integration identifier.`);
30
+ }
31
+ return normalized;
32
+ }
33
+ function requireRequirementKind(value, label) {
34
+ const normalized = requireContractString(value, label, 64);
35
+ if (!REQUIREMENT_KIND_PATTERN.test(normalized)) {
36
+ throw new Error(`${label} must use a lowercase resource kind.`);
37
+ }
38
+ return normalized;
39
+ }
40
+ function assertOnlyKeys(record, allowedKeys, label) {
41
+ const allowed = new Set(allowedKeys);
42
+ const unknownKey = Object.keys(record).find((key) => !allowed.has(key));
43
+ if (unknownKey) {
44
+ throw new Error(`${label}.${unknownKey} is not supported.`);
45
+ }
46
+ }
47
+ function normalizeIntegrationRequirements(value) {
48
+ if (value === undefined)
49
+ return undefined;
50
+ if (!Array.isArray(value)) {
51
+ throw new Error("Schedule integrationRequirements must be an array.");
52
+ }
53
+ if (value.length > 20) {
54
+ throw new Error("Schedule integrationRequirements must contain at most 20 entries.");
55
+ }
56
+ const integrations = new Set();
57
+ return value.map((requirement, index) => {
58
+ if (!requirement || typeof requirement !== "object" || Array.isArray(requirement)) {
59
+ throw new Error(`Schedule integrationRequirements[${index}] must be an object.`);
60
+ }
61
+ const record = requirement;
62
+ const label = `Schedule integrationRequirements[${index}]`;
63
+ assertOnlyKeys(record, ["integration", "requiredScopes", "resources"], label);
64
+ const integration = requireIntegrationName(record.integration, `${label}.integration`);
65
+ const integrationKey = integration.toLowerCase();
66
+ if (integrations.has(integrationKey)) {
67
+ throw new Error(`Schedule integrationRequirements contains duplicate integration ${integration}.`);
68
+ }
69
+ integrations.add(integrationKey);
70
+ const rawRequiredScopes = record.requiredScopes ?? [];
71
+ if (!Array.isArray(rawRequiredScopes)) {
72
+ throw new Error(`${label}.requiredScopes must be an array.`);
73
+ }
74
+ if (rawRequiredScopes.length > 50) {
75
+ throw new Error(`${label}.requiredScopes must contain at most 50 entries.`);
76
+ }
77
+ const requiredScopes = rawRequiredScopes.map((scope, scopeIndex) => requireContractString(scope, `${label}.requiredScopes[${scopeIndex}]`, 255));
78
+ const rawResources = record.resources ?? [];
79
+ if (!Array.isArray(rawResources)) {
80
+ throw new Error(`${label}.resources must be an array.`);
81
+ }
82
+ if (rawResources.length > 50) {
83
+ throw new Error(`${label}.resources must contain at most 50 entries.`);
84
+ }
85
+ const resources = rawResources.map((resource, resourceIndex) => normalizeIntegrationResource(resource, index, resourceIndex));
86
+ return { integration, requiredScopes, resources };
87
+ });
88
+ }
89
+ function normalizeIntegrationResource(resource, requirementIndex, resourceIndex) {
90
+ if (!resource || typeof resource !== "object" || Array.isArray(resource)) {
91
+ throw new Error(`Schedule integrationRequirements[${requirementIndex}].resources[${resourceIndex}] must be an object.`);
92
+ }
93
+ const record = resource;
94
+ const label = `Schedule integrationRequirements[${requirementIndex}].resources[${resourceIndex}]`;
95
+ assertOnlyKeys(record, ["kind", "id", "parent"], label);
96
+ const kind = requireRequirementKind(record.kind, `${label}.kind`);
97
+ const id = requireContractString(record.id, `${label}.id`, 512);
98
+ const parent = record.parent;
99
+ const normalizedParent = normalizeIntegrationResourceParent(parent, requirementIndex, resourceIndex);
100
+ return {
101
+ kind,
102
+ id,
103
+ ...(normalizedParent === undefined ? {} : { parent: normalizedParent }),
104
+ };
105
+ }
106
+ function normalizeIntegrationResourceParent(value, requirementIndex, resourceIndex) {
107
+ if (value === undefined)
108
+ return undefined;
109
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
110
+ throw new Error(`Schedule integrationRequirements[${requirementIndex}].resources[${resourceIndex}].parent must be an object.`);
111
+ }
112
+ const record = value;
113
+ const label = `Schedule integrationRequirements[${requirementIndex}].resources[${resourceIndex}].parent`;
114
+ assertOnlyKeys(record, ["kind", "id"], label);
115
+ return {
116
+ kind: requireRequirementKind(record.kind, `${label}.kind`),
117
+ id: requireContractString(record.id, `${label}.id`, 512),
118
+ };
119
+ }
17
120
  export function schedule(config) {
18
121
  const id = requireString(config.id, "Schedule id");
19
122
  validateTriggerId(id, "Schedule");
@@ -30,6 +133,7 @@ export function schedule(config) {
30
133
  validatePositiveInteger(config.backoffLimit, "Schedule backoffLimit");
31
134
  validatePositiveInteger(config.maxRuns, "Schedule maxRuns");
32
135
  assertSerializable(config.input, "Schedule input");
136
+ const integrationRequirements = normalizeIntegrationRequirements(config.integrationRequirements);
33
137
  return {
34
138
  id,
35
139
  ...(config.name === undefined ? {} : { name: config.name }),
@@ -44,5 +148,6 @@ export function schedule(config) {
44
148
  ? {}
45
149
  : { concurrencyPolicy: config.concurrencyPolicy }),
46
150
  ...(config.maxRuns === undefined ? {} : { maxRuns: config.maxRuns }),
151
+ ...(integrationRequirements === undefined ? {} : { integrationRequirements }),
47
152
  };
48
153
  }
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import "../../_dnt.polyfills.js";
20
20
  export { schedule } from "./factory.js";
21
- export type { ScheduleConcurrencyPolicy, ScheduleConfig, ScheduleDefinition } from "./types.js";
21
+ export type { ScheduleConcurrencyPolicy, ScheduleConfig, ScheduleDefinition, ScheduleIntegrationRequirement, ScheduleIntegrationRequirementConfig, ScheduleIntegrationResource, ScheduleIntegrationResourceIdentity, } from "./types.js";
22
22
  export { isScheduleDefinition } from "./types.js";
23
23
  export { discoverSchedules } from "./discovery.js";
24
24
  export type { ScheduleDiscoveryOptions, ScheduleDiscoveryResult } from "./discovery.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/schedule/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,yBAAyB,CAAC;AAGjC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,YAAY,EAAE,yBAAyB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChG,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,YAAY,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/schedule/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,yBAAyB,CAAC;AAGjC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,YAAY,EACV,yBAAyB,EACzB,cAAc,EACd,kBAAkB,EAClB,8BAA8B,EAC9B,oCAAoC,EACpC,2BAA2B,EAC3B,mCAAmC,GACpC,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,YAAY,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,5 +1,22 @@
1
1
  import type { TriggerTarget } from "../trigger/target.js";
2
2
  export type ScheduleConcurrencyPolicy = "Allow" | "Forbid" | "Replace";
3
+ export interface ScheduleIntegrationResourceIdentity {
4
+ kind: string;
5
+ id: string;
6
+ }
7
+ export interface ScheduleIntegrationResource extends ScheduleIntegrationResourceIdentity {
8
+ parent?: ScheduleIntegrationResourceIdentity;
9
+ }
10
+ export interface ScheduleIntegrationRequirement {
11
+ integration: string;
12
+ requiredScopes: string[];
13
+ resources: ScheduleIntegrationResource[];
14
+ }
15
+ export interface ScheduleIntegrationRequirementConfig {
16
+ integration: string;
17
+ requiredScopes?: string[];
18
+ resources?: ScheduleIntegrationResource[];
19
+ }
3
20
  export interface ScheduleDefinition {
4
21
  id: string;
5
22
  name?: string;
@@ -12,10 +29,12 @@ export interface ScheduleDefinition {
12
29
  backoffLimit?: number;
13
30
  concurrencyPolicy?: ScheduleConcurrencyPolicy;
14
31
  maxRuns?: number;
32
+ integrationRequirements?: ScheduleIntegrationRequirement[];
15
33
  }
16
- export type ScheduleConfig = Omit<ScheduleDefinition, "schedule"> & {
34
+ export type ScheduleConfig = Omit<ScheduleDefinition, "schedule" | "integrationRequirements"> & {
17
35
  cron?: string;
18
36
  schedule?: string;
37
+ integrationRequirements?: ScheduleIntegrationRequirementConfig[];
19
38
  };
20
39
  export declare function isScheduleDefinition(value: unknown): value is ScheduleDefinition;
21
40
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/src/schedule/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,MAAM,yBAAyB,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEvE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,yBAAyB,CAAC;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC,GAAG;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAShF"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/src/schedule/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,MAAM,yBAAyB,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEvE,MAAM,WAAW,mCAAmC;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,2BAA4B,SAAQ,mCAAmC;IACtF,MAAM,CAAC,EAAE,mCAAmC,CAAC;CAC9C;AAED,MAAM,WAAW,8BAA8B;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,EAAE,2BAA2B,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,oCAAoC;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,2BAA2B,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,yBAAyB,CAAC;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uBAAuB,CAAC,EAAE,8BAA8B,EAAE,CAAC;CAC5D;AAED,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,yBAAyB,CAAC,GAAG;IAC9F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uBAAuB,CAAC,EAAE,oCAAoC,EAAE,CAAC;CAClE,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,kBAAkB,CAUhF"}
@@ -5,5 +5,69 @@ export function isScheduleDefinition(value) {
5
5
  return (typeof definition.id === "string" &&
6
6
  typeof definition.schedule === "string" &&
7
7
  definition.target !== null &&
8
- typeof definition.target === "object");
8
+ typeof definition.target === "object" &&
9
+ isScheduleIntegrationRequirements(definition.integrationRequirements));
10
+ }
11
+ function isScheduleIntegrationRequirements(value) {
12
+ if (value === undefined)
13
+ return true;
14
+ if (!Array.isArray(value) || value.length > 20)
15
+ return false;
16
+ const integrations = new Set();
17
+ for (const requirement of value) {
18
+ if (!requirement || typeof requirement !== "object" || Array.isArray(requirement)) {
19
+ return false;
20
+ }
21
+ const record = requirement;
22
+ const integration = isIntegrationName(record.integration) ? record.integration : undefined;
23
+ if (integration === undefined ||
24
+ !hasOnlyKeys(record, ["integration", "requiredScopes", "resources"]) ||
25
+ integrations.has(integration.toLowerCase()) ||
26
+ !isStringArray(record.requiredScopes) ||
27
+ !isScheduleIntegrationResources(record.resources)) {
28
+ return false;
29
+ }
30
+ integrations.add(integration.toLowerCase());
31
+ }
32
+ return true;
33
+ }
34
+ function isScheduleIntegrationResources(value) {
35
+ if (!Array.isArray(value) || value.length > 50)
36
+ return false;
37
+ return value.every((resource) => {
38
+ if (!resource || typeof resource !== "object" || Array.isArray(resource))
39
+ return false;
40
+ const record = resource;
41
+ return (hasOnlyKeys(record, ["kind", "id", "parent"]) &&
42
+ isRequirementKind(record.kind) &&
43
+ isBoundedString(record.id, 512) &&
44
+ (record.parent === undefined || isScheduleIntegrationResourceIdentity(record.parent)));
45
+ });
46
+ }
47
+ function isScheduleIntegrationResourceIdentity(value) {
48
+ if (!value || typeof value !== "object" || Array.isArray(value))
49
+ return false;
50
+ const record = value;
51
+ return hasOnlyKeys(record, ["kind", "id"]) &&
52
+ isRequirementKind(record.kind) && isBoundedString(record.id, 512);
53
+ }
54
+ function hasOnlyKeys(record, allowedKeys) {
55
+ const allowed = new Set(allowedKeys);
56
+ return Object.keys(record).every((key) => allowed.has(key));
57
+ }
58
+ function isStringArray(value) {
59
+ return Array.isArray(value) && value.length <= 50 &&
60
+ value.every((item) => isBoundedString(item, 255));
61
+ }
62
+ function isNonEmptyString(value) {
63
+ return typeof value === "string" && value.trim().length > 0;
64
+ }
65
+ function isBoundedString(value, maxLength) {
66
+ return isNonEmptyString(value) && value.trim().length <= maxLength;
67
+ }
68
+ function isIntegrationName(value) {
69
+ return isBoundedString(value, 255) && /^[a-z0-9][a-z0-9_-]*$/.test(value.trim());
70
+ }
71
+ function isRequirementKind(value) {
72
+ return isBoundedString(value, 64) && /^[a-z][a-z0-9_-]*$/.test(value.trim());
9
73
  }
@@ -1,3 +1,3 @@
1
1
  /** Shared version value. */
2
- export declare const VERSION = "0.1.1063";
2
+ export declare const VERSION = "0.1.1064";
3
3
  //# sourceMappingURL=version-constant.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
3
  /** Shared version value. */
4
- export const VERSION = "0.1.1063";
4
+ export const VERSION = "0.1.1064";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veryfront",
3
- "version": "0.1.1063",
3
+ "version": "0.1.1064",
4
4
  "description": "The simplest way to build AI-powered apps",
5
5
  "keywords": [
6
6
  "react",
@@ -328,10 +328,10 @@
328
328
  "@types/react": "19.2.14",
329
329
  "@types/react-dom": "19.2.3",
330
330
  "ws": "8.21.0",
331
- "@veryfront/ext-bundler-esbuild": "0.1.1063",
332
- "@veryfront/ext-content-mdx": "0.1.1063",
333
- "@veryfront/ext-css-tailwind": "0.1.1063",
334
- "@veryfront/ext-parser-babel": "0.1.1063"
331
+ "@veryfront/ext-bundler-esbuild": "0.1.1064",
332
+ "@veryfront/ext-content-mdx": "0.1.1064",
333
+ "@veryfront/ext-css-tailwind": "0.1.1064",
334
+ "@veryfront/ext-parser-babel": "0.1.1064"
335
335
  },
336
336
  "devDependencies": {
337
337
  "@types/node": "20.9.0"