canopycms 0.0.55 → 0.0.57

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 (123) hide show
  1. package/dist/api/branch.d.ts +6 -0
  2. package/dist/api/branch.d.ts.map +1 -1
  3. package/dist/api/branch.js +63 -19
  4. package/dist/api/branch.js.map +1 -1
  5. package/dist/api/content.d.ts +2 -2
  6. package/dist/api/entries.d.ts.map +1 -1
  7. package/dist/api/entries.js +10 -2
  8. package/dist/api/entries.js.map +1 -1
  9. package/dist/api/github-sync.d.ts.map +1 -1
  10. package/dist/api/github-sync.js +28 -7
  11. package/dist/api/github-sync.js.map +1 -1
  12. package/dist/auth/caching-auth-plugin.d.ts +8 -1
  13. package/dist/auth/caching-auth-plugin.d.ts.map +1 -1
  14. package/dist/auth/caching-auth-plugin.js +6 -1
  15. package/dist/auth/caching-auth-plugin.js.map +1 -1
  16. package/dist/auth/plugin.d.ts +19 -10
  17. package/dist/auth/plugin.d.ts.map +1 -1
  18. package/dist/auth/plugin.js +12 -10
  19. package/dist/auth/plugin.js.map +1 -1
  20. package/dist/branch-metadata.d.ts +71 -6
  21. package/dist/branch-metadata.d.ts.map +1 -1
  22. package/dist/branch-metadata.js +132 -116
  23. package/dist/branch-metadata.js.map +1 -1
  24. package/dist/branch-registry.d.ts +78 -9
  25. package/dist/branch-registry.d.ts.map +1 -1
  26. package/dist/branch-registry.js +155 -29
  27. package/dist/branch-registry.js.map +1 -1
  28. package/dist/branch-schema-cache.d.ts +126 -11
  29. package/dist/branch-schema-cache.d.ts.map +1 -1
  30. package/dist/branch-schema-cache.js +208 -58
  31. package/dist/branch-schema-cache.js.map +1 -1
  32. package/dist/build/generate-ai-content.d.ts.map +1 -1
  33. package/dist/build/generate-ai-content.js +7 -0
  34. package/dist/build/generate-ai-content.js.map +1 -1
  35. package/dist/cli/generate-ai-content.js +2358 -1556
  36. package/dist/cli/init.d.ts.map +1 -1
  37. package/dist/cli/init.js +16 -8
  38. package/dist/cli/init.js.map +1 -1
  39. package/dist/cli/migrate.d.ts.map +1 -1
  40. package/dist/cli/migrate.js +25 -0
  41. package/dist/cli/migrate.js.map +1 -1
  42. package/dist/cli/sync.js +5 -5
  43. package/dist/cli/sync.js.map +1 -1
  44. package/dist/cli/templates.d.ts.map +1 -1
  45. package/dist/cli/templates.js +6 -3
  46. package/dist/cli/templates.js.map +1 -1
  47. package/dist/comment-store.d.ts +45 -15
  48. package/dist/comment-store.d.ts.map +1 -1
  49. package/dist/comment-store.js +80 -101
  50. package/dist/comment-store.js.map +1 -1
  51. package/dist/config/helpers.d.ts.map +1 -1
  52. package/dist/config/helpers.js +3 -0
  53. package/dist/config/helpers.js.map +1 -1
  54. package/dist/config/schemas/config.d.ts +3 -4
  55. package/dist/config/schemas/config.d.ts.map +1 -1
  56. package/dist/config/schemas/config.js +6 -3
  57. package/dist/config/schemas/config.js.map +1 -1
  58. package/dist/config/types.d.ts +6 -1
  59. package/dist/config/types.d.ts.map +1 -1
  60. package/dist/config-test.d.ts +5 -3
  61. package/dist/config-test.d.ts.map +1 -1
  62. package/dist/config-test.js +9 -1
  63. package/dist/config-test.js.map +1 -1
  64. package/dist/content-index-generation.d.ts +40 -4
  65. package/dist/content-index-generation.d.ts.map +1 -1
  66. package/dist/content-index-generation.js +70 -73
  67. package/dist/content-index-generation.js.map +1 -1
  68. package/dist/content-listing.d.ts +3 -1
  69. package/dist/content-listing.d.ts.map +1 -1
  70. package/dist/content-listing.js +1 -0
  71. package/dist/content-listing.js.map +1 -1
  72. package/dist/content-store.d.ts +63 -0
  73. package/dist/content-store.d.ts.map +1 -1
  74. package/dist/content-store.js +421 -184
  75. package/dist/content-store.js.map +1 -1
  76. package/dist/editor/Editor.d.ts.map +1 -1
  77. package/dist/editor/Editor.js +19 -15
  78. package/dist/editor/Editor.js.map +1 -1
  79. package/dist/editor/hooks/useDraftManager.d.ts +7 -0
  80. package/dist/editor/hooks/useDraftManager.d.ts.map +1 -1
  81. package/dist/editor/hooks/useDraftManager.js +69 -31
  82. package/dist/editor/hooks/useDraftManager.js.map +1 -1
  83. package/dist/editor/hooks/useSchemaManager.d.ts +34 -10
  84. package/dist/editor/hooks/useSchemaManager.d.ts.map +1 -1
  85. package/dist/editor/hooks/useSchemaManager.js +20 -24
  86. package/dist/editor/hooks/useSchemaManager.js.map +1 -1
  87. package/dist/editor/schema-editor/CollectionEditor.d.ts +6 -5
  88. package/dist/editor/schema-editor/CollectionEditor.d.ts.map +1 -1
  89. package/dist/editor/schema-editor/CollectionEditor.js +9 -4
  90. package/dist/editor/schema-editor/CollectionEditor.js.map +1 -1
  91. package/dist/git-manager.d.ts +15 -10
  92. package/dist/git-manager.d.ts.map +1 -1
  93. package/dist/git-manager.js +16 -11
  94. package/dist/git-manager.js.map +1 -1
  95. package/dist/github-service.d.ts +63 -7
  96. package/dist/github-service.d.ts.map +1 -1
  97. package/dist/github-service.js +147 -39
  98. package/dist/github-service.js.map +1 -1
  99. package/dist/resource-generation.d.ts +54 -0
  100. package/dist/resource-generation.d.ts.map +1 -0
  101. package/dist/resource-generation.js +156 -0
  102. package/dist/resource-generation.js.map +1 -0
  103. package/dist/schema/schema-store.d.ts +29 -2
  104. package/dist/schema/schema-store.d.ts.map +1 -1
  105. package/dist/schema/schema-store.js +47 -8
  106. package/dist/schema/schema-store.js.map +1 -1
  107. package/dist/services.js +4 -0
  108. package/dist/services.js.map +1 -1
  109. package/dist/static/index.d.ts +32 -0
  110. package/dist/static/index.d.ts.map +1 -1
  111. package/dist/static/index.js +90 -0
  112. package/dist/static/index.js.map +1 -1
  113. package/dist/sync-core.js +2 -2
  114. package/dist/sync-core.js.map +1 -1
  115. package/dist/utils/occ-json-write.d.ts +100 -0
  116. package/dist/utils/occ-json-write.d.ts.map +1 -0
  117. package/dist/utils/occ-json-write.js +234 -0
  118. package/dist/utils/occ-json-write.js.map +1 -0
  119. package/dist/worker/cms-worker.d.ts +6 -3
  120. package/dist/worker/cms-worker.d.ts.map +1 -1
  121. package/dist/worker/cms-worker.js +42 -41
  122. package/dist/worker/cms-worker.js.map +1 -1
  123. package/package.json +2 -1
@@ -1,4 +1,28 @@
1
+ import { Octokit } from '@octokit/rest';
1
2
  import type { CanopyConfig } from './config';
3
+ /**
4
+ * Retry primary rate limits at most twice and only for short waits; beyond
5
+ * that, task-level retry/backoff (worker) or the caller's error path takes
6
+ * over. The worker's task timeout would abort a longer in-request wait
7
+ * anyway.
8
+ */
9
+ export declare const shouldRetryRateLimit: (retryAfter: number, retryCount: number) => boolean;
10
+ /**
11
+ * Secondary (abuse-detection) rate limits are stricter to trip and usually
12
+ * signal we're hammering the API too fast — retry at most once, and only for
13
+ * a short wait.
14
+ */
15
+ export declare const shouldRetrySecondaryRateLimit: (retryAfter: number, retryCount: number) => boolean;
16
+ /**
17
+ * Create an Octokit instance with the throttling plugin attached, so it
18
+ * proactively respects GitHub's `retry-after` guidance on rate limits
19
+ * instead of failing immediately (see cms-worker.ts isPermanentTaskFailure
20
+ * for the safety net this doesn't cover: exhausted plugin retries and
21
+ * errors the plugin never sees, like non-403 network failures).
22
+ */
23
+ export declare function createCanopyOctokit(options: {
24
+ auth: string;
25
+ }): Octokit;
2
26
  export interface GitHubServiceOptions {
3
27
  token: string;
4
28
  owner: string;
@@ -18,6 +42,39 @@ export interface PullRequestDetails {
18
42
  merged: boolean;
19
43
  draft: boolean;
20
44
  }
45
+ export interface CreateOrUpdatePullRequestParams {
46
+ octokit: Octokit;
47
+ owner: string;
48
+ repo: string;
49
+ head: string;
50
+ base: string;
51
+ title: string;
52
+ body: string;
53
+ /** Convert a pre-existing draft PR to ready-for-review after updating it. */
54
+ markReadyIfDraft?: boolean;
55
+ /** Forwarded to all GitHub requests (worker task-timeout abort). */
56
+ signal?: AbortSignal;
57
+ }
58
+ /**
59
+ * Create or update a pull request (idempotent — GIT-H1).
60
+ *
61
+ * If an open PR already exists from head to base, update it and return its
62
+ * number/url instead of erroring. This makes PR submission safely
63
+ * retryable: if a caller crashes after GitHub creates the PR but before it
64
+ * persists the returned PR number, calling this again recovers the
65
+ * existing PR instead of hitting the 422 that a blind create would throw on
66
+ * a duplicate, which previously wedged the branch in 'sync-failed'
67
+ * permanently.
68
+ *
69
+ * Shared by `GitHubService.createOrUpdatePR` (direct-API callers) and the
70
+ * worker's `push-and-create-or-update-pr` task, so the list->tiebreak->
71
+ * update/create logic and the draft->ready conversion live in one place.
72
+ */
73
+ export declare function createOrUpdatePullRequest(params: CreateOrUpdatePullRequestParams): Promise<{
74
+ number: number;
75
+ url: string;
76
+ created: boolean;
77
+ }>;
21
78
  /**
22
79
  * Service for interacting with GitHub API (pull requests, branches, etc.)
23
80
  */
@@ -41,19 +98,18 @@ export declare class GitHubService {
41
98
  /**
42
99
  * Create or update a pull request (idempotent — GIT-H1).
43
100
  *
44
- * If an open PR already exists from head to base, update it and return its
45
- * number/url instead of erroring. This makes PR submission safely
46
- * retryable: if a caller crashes after GitHub creates the PR but before it
47
- * persists the returned PR number, calling this again recovers the
48
- * existing PR instead of hitting the 422 that `createPullRequest` would
49
- * throw on a duplicate, which previously wedged the branch in
50
- * 'sync-failed' permanently.
101
+ * Thin delegate to the module-level `createOrUpdatePullRequest` helper
102
+ * (shared with the worker's `push-and-create-or-update-pr` task) bound to
103
+ * this instance's octokit/owner/repo. See that function's doc comment for
104
+ * the idempotency rationale.
51
105
  */
52
106
  createOrUpdatePR(options: {
53
107
  head: string;
54
108
  base: string;
55
109
  title: string;
56
110
  body: string;
111
+ /** Convert a pre-existing draft PR to ready-for-review after updating it. */
112
+ markReadyIfDraft?: boolean;
57
113
  }): Promise<{
58
114
  number: number;
59
115
  url: string;
@@ -1 +1 @@
1
- {"version":3,"file":"github-service.d.ts","sourceRoot":"","sources":["../src/github-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAG5C,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,GAAG,QAAQ,CAAA;IACxB,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,EAAE,OAAO,CAAA;CACf;AAED;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,UAAU,CAAQ;gBAEd,OAAO,EAAE,oBAAoB;IAOzC;;OAEG;IACG,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAiB9F;;OAEG;IACG,iBAAiB,CACrB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC,GAC3D,OAAO,CAAC,IAAI,CAAC;IAUhB;;;;;;;;;;OAUG;IACG,gBAAgB,CAAC,OAAO,EAAE;QAC9B,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;KACb,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IAgD5C;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgBnE;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrD;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBrD;;OAEG;IACG,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASvD;;OAEG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQrD;;OAEG;YACW,oBAAoB;IASlC;;;;;;;OAOG;IACH,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CA4B1E;AAED;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,YAAY,EACpB,YAAY,MAAM,KACjB,aAAa,GAAG,IAmClB,CAAA"}
1
+ {"version":3,"file":"github-service.d.ts","sourceRoot":"","sources":["../src/github-service.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,149 @@
1
1
  import { Octokit } from '@octokit/rest';
2
+ import { throttling } from '@octokit/plugin-throttling';
2
3
  import { operatingStrategy } from './operating-mode/index.js';
4
+ import { getErrorMessage } from './utils/error.js';
5
+ const ThrottledOctokit = Octokit.plugin(throttling);
6
+ /**
7
+ * Retry primary rate limits at most twice and only for short waits; beyond
8
+ * that, task-level retry/backoff (worker) or the caller's error path takes
9
+ * over. The worker's task timeout would abort a longer in-request wait
10
+ * anyway.
11
+ */
12
+ export const shouldRetryRateLimit = (retryAfter, retryCount) => retryCount < 2 && retryAfter <= 60;
13
+ /**
14
+ * Secondary (abuse-detection) rate limits are stricter to trip and usually
15
+ * signal we're hammering the API too fast — retry at most once, and only for
16
+ * a short wait.
17
+ */
18
+ export const shouldRetrySecondaryRateLimit = (retryAfter, retryCount) => retryCount < 1 && retryAfter <= 60;
19
+ /**
20
+ * Create an Octokit instance with the throttling plugin attached, so it
21
+ * proactively respects GitHub's `retry-after` guidance on rate limits
22
+ * instead of failing immediately (see cms-worker.ts isPermanentTaskFailure
23
+ * for the safety net this doesn't cover: exhausted plugin retries and
24
+ * errors the plugin never sees, like non-403 network failures).
25
+ */
26
+ export function createCanopyOctokit(options) {
27
+ return new ThrottledOctokit({
28
+ auth: options.auth,
29
+ throttle: {
30
+ onRateLimit: (retryAfter, requestOptions, _octokit, retryCount) => {
31
+ console.warn(`CanopyCMS: GitHub primary rate limit hit for ${requestOptions.method} ${requestOptions.url} ` +
32
+ `(retryAfter=${retryAfter}s, retryCount=${retryCount})`);
33
+ return shouldRetryRateLimit(retryAfter, retryCount);
34
+ },
35
+ onSecondaryRateLimit: (retryAfter, requestOptions, _octokit, retryCount) => {
36
+ console.warn(`CanopyCMS: GitHub secondary rate limit hit for ${requestOptions.method} ${requestOptions.url} ` +
37
+ `(retryAfter=${retryAfter}s, retryCount=${retryCount})`);
38
+ return shouldRetrySecondaryRateLimit(retryAfter, retryCount);
39
+ },
40
+ },
41
+ });
42
+ }
43
+ /**
44
+ * Create or update a pull request (idempotent — GIT-H1).
45
+ *
46
+ * If an open PR already exists from head to base, update it and return its
47
+ * number/url instead of erroring. This makes PR submission safely
48
+ * retryable: if a caller crashes after GitHub creates the PR but before it
49
+ * persists the returned PR number, calling this again recovers the
50
+ * existing PR instead of hitting the 422 that a blind create would throw on
51
+ * a duplicate, which previously wedged the branch in 'sync-failed'
52
+ * permanently.
53
+ *
54
+ * Shared by `GitHubService.createOrUpdatePR` (direct-API callers) and the
55
+ * worker's `push-and-create-or-update-pr` task, so the list->tiebreak->
56
+ * update/create logic and the draft->ready conversion live in one place.
57
+ */
58
+ export async function createOrUpdatePullRequest(params) {
59
+ const { octokit, owner, repo, head, base, title, body, markReadyIfDraft, signal } = params;
60
+ // CONDITIONAL spread: when no signal is passed, request objects below are
61
+ // byte-identical to the pre-refactor call sites (exact toHaveBeenCalledWith
62
+ // assertions in github-service.test.ts depend on this).
63
+ const requestOption = signal ? { request: { signal } } : {};
64
+ // Check if an open PR already exists for this head/base
65
+ const existingPRs = await octokit.pulls.list({
66
+ owner,
67
+ repo,
68
+ head: `${owner}:${head}`,
69
+ base,
70
+ state: 'open',
71
+ ...requestOption,
72
+ });
73
+ if (existingPRs.data.length > 0) {
74
+ // GIT-M5: GitHub disallows more than one open PR for a given
75
+ // head+base pair, so this should always be a single match. Guard
76
+ // against blindly trusting array order anyway — if more than one is
77
+ // ever returned, warn and prefer the most recently updated instead of
78
+ // an arbitrary one.
79
+ let existing = existingPRs.data[0];
80
+ if (existingPRs.data.length > 1) {
81
+ existing = [...existingPRs.data].sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())[0];
82
+ console.warn(`CanopyCMS: Found ${existingPRs.data.length} open PRs for ${head} -> ${base}; updating the most recently updated (#${existing.number})`);
83
+ }
84
+ await octokit.pulls.update({
85
+ owner,
86
+ repo,
87
+ pull_number: existing.number,
88
+ title,
89
+ body,
90
+ ...requestOption,
91
+ });
92
+ // Only a pre-existing PR can be a draft on this path — pulls.create
93
+ // above is never called with draft: true, so a newly created PR is
94
+ // never draft and needs no conversion.
95
+ if (markReadyIfDraft && existing.draft) {
96
+ // Use GraphQL API for draft conversion (not available in REST API).
97
+ // Read the node id straight off the list payload — no extra pulls.get.
98
+ //
99
+ // Best-effort: the push + pulls.update above already succeeded, so the
100
+ // submit itself is done. A fine-grained token that can update PRs but
101
+ // lacks this mutation's scope throws a GraphqlResponseError, which
102
+ // carries no numeric HTTP status (the GraphQL endpoint responds 200
103
+ // even for a mutation-level failure) — the worker's
104
+ // isPermanentTaskFailure would classify that as transient and retry
105
+ // the whole re-push until the retry cap, wedging the branch in
106
+ // 'sync-failed' even though the PR already exists and is current.
107
+ // Warn and continue instead of letting this sink an already-succeeded
108
+ // submit.
109
+ try {
110
+ await octokit.graphql(`
111
+ mutation($pullRequestId: ID!) {
112
+ markPullRequestReadyForReview(input: {pullRequestId: $pullRequestId}) {
113
+ pullRequest {
114
+ id
115
+ }
116
+ }
117
+ }
118
+ `, {
119
+ pullRequestId: existing.node_id,
120
+ ...requestOption,
121
+ });
122
+ }
123
+ catch (err) {
124
+ console.warn(`CanopyCMS: Failed to convert PR #${existing.number} to ready for review (the PR update itself succeeded; continuing):`, getErrorMessage(err));
125
+ }
126
+ }
127
+ return { number: existing.number, url: existing.html_url, created: false };
128
+ }
129
+ // Create new PR
130
+ const pr = await octokit.pulls.create({
131
+ owner,
132
+ repo,
133
+ head,
134
+ base,
135
+ title,
136
+ body,
137
+ ...requestOption,
138
+ });
139
+ return { number: pr.data.number, url: pr.data.html_url, created: true };
140
+ }
3
141
  /**
4
142
  * Service for interacting with GitHub API (pull requests, branches, etc.)
5
143
  */
6
144
  export class GitHubService {
7
145
  constructor(options) {
8
- this.octokit = new Octokit({ auth: options.token });
146
+ this.octokit = createCanopyOctokit({ auth: options.token });
9
147
  this.owner = options.owner;
10
148
  this.repo = options.repo;
11
149
  this.baseBranch = options.baseBranch ?? 'main';
@@ -43,53 +181,23 @@ export class GitHubService {
43
181
  /**
44
182
  * Create or update a pull request (idempotent — GIT-H1).
45
183
  *
46
- * If an open PR already exists from head to base, update it and return its
47
- * number/url instead of erroring. This makes PR submission safely
48
- * retryable: if a caller crashes after GitHub creates the PR but before it
49
- * persists the returned PR number, calling this again recovers the
50
- * existing PR instead of hitting the 422 that `createPullRequest` would
51
- * throw on a duplicate, which previously wedged the branch in
52
- * 'sync-failed' permanently.
184
+ * Thin delegate to the module-level `createOrUpdatePullRequest` helper
185
+ * (shared with the worker's `push-and-create-or-update-pr` task) bound to
186
+ * this instance's octokit/owner/repo. See that function's doc comment for
187
+ * the idempotency rationale.
53
188
  */
54
189
  async createOrUpdatePR(options) {
55
- // Check if an open PR already exists for this head/base
56
- const existingPRs = await this.octokit.pulls.list({
57
- owner: this.owner,
58
- repo: this.repo,
59
- head: `${this.owner}:${options.head}`,
60
- base: options.base,
61
- state: 'open',
62
- });
63
- if (existingPRs.data.length > 0) {
64
- // GIT-M5: GitHub disallows more than one open PR for a given
65
- // head+base pair, so this should always be a single match. Guard
66
- // against blindly trusting array order anyway — if more than one is
67
- // ever returned, warn and prefer the most recently updated instead of
68
- // an arbitrary one.
69
- let pr = existingPRs.data[0];
70
- if (existingPRs.data.length > 1) {
71
- pr = [...existingPRs.data].sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime())[0];
72
- console.warn(`CanopyCMS: Found ${existingPRs.data.length} open PRs for ${options.head} -> ${options.base}; updating the most recently updated (#${pr.number})`);
73
- }
74
- await this.octokit.pulls.update({
75
- owner: this.owner,
76
- repo: this.repo,
77
- pull_number: pr.number,
78
- title: options.title,
79
- body: options.body,
80
- });
81
- return { number: pr.number, url: pr.html_url };
82
- }
83
- // Create new PR
84
- const pr = await this.octokit.pulls.create({
190
+ const result = await createOrUpdatePullRequest({
191
+ octokit: this.octokit,
85
192
  owner: this.owner,
86
193
  repo: this.repo,
87
194
  head: options.head,
88
195
  base: options.base,
89
196
  title: options.title,
90
197
  body: options.body,
198
+ markReadyIfDraft: options.markReadyIfDraft,
91
199
  });
92
- return { number: pr.data.number, url: pr.data.html_url };
200
+ return { number: result.number, url: result.url };
93
201
  }
94
202
  /**
95
203
  * Get pull request details
@@ -1 +1 @@
1
- {"version":3,"file":"github-service.js","sourceRoot":"","sources":["../src/github-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"github-service.js","sourceRoot":"","sources":["../src/github-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ /** Absolute path of the generation marker for `resource` under `root`. */
2
+ export declare function resourceGenerationPath(root: string, resource: string): string;
3
+ export interface BumpResourceGenerationOptions {
4
+ /**
5
+ * When true, a failed bump rethrows instead of being logged and swallowed.
6
+ * Use this for callers where a lost bump means indefinitely stale durable
7
+ * data with no bounding backstop (e.g. a registry's invalidate()) — those
8
+ * callers must not silently succeed while leaving stale readers unaware.
9
+ * Default false (hint flavor): log-warn and swallow, since the mutation
10
+ * itself is already durable and a lost bump only degrades to pre-marker
11
+ * staleness behavior plus whatever backstop the consumer has.
12
+ */
13
+ mustSucceed?: boolean;
14
+ }
15
+ /**
16
+ * Record on disk that `resource` changed under `root`, so consumers in OTHER
17
+ * processes rebuild. Must be called AFTER the filesystem mutation.
18
+ *
19
+ * Returns the token written, or null if the write failed and `mustSucceed`
20
+ * was not set (logged and swallowed).
21
+ */
22
+ export declare function bumpResourceGeneration(root: string, resource: string, options?: BumpResourceGenerationOptions): Promise<string | null>;
23
+ /**
24
+ * Result of reading a generation marker. `ok: false` means the read failed
25
+ * for a reason OTHER than "marker doesn't exist yet" — callers should treat
26
+ * this as "force regenerate" rather than folding it into a token value,
27
+ * because a legitimate snapshot can embed `token: null` (a fresh clone,
28
+ * regenerated before any bump ever occurred) and that must stay
29
+ * distinguishable from "we don't actually know the current token".
30
+ */
31
+ export type GenerationReadResult = {
32
+ ok: true;
33
+ token: string | null;
34
+ } | {
35
+ ok: false;
36
+ };
37
+ /**
38
+ * Read the current generation token for `resource` under `root`.
39
+ *
40
+ * ENOENT maps to `{ok: true, token: null}` — a valid "never bumped" state,
41
+ * distinct from every real token. Any OTHER read error maps to `{ok: false}`
42
+ * (logged) so callers can force-regenerate instead of trusting a null they
43
+ * cannot actually attribute to "never bumped".
44
+ */
45
+ export declare function readResourceGeneration(root: string, resource: string): Promise<GenerationReadResult>;
46
+ /**
47
+ * True when a previously-captured snapshot token is still current: the read
48
+ * succeeded AND its token matches the snapshot's. A failed read (`ok: false`)
49
+ * is never current, even if the snapshot's token happens to be null — an
50
+ * unreadable marker means we cannot vouch for freshness either way, and the
51
+ * safe default is to treat it as stale.
52
+ */
53
+ export declare function isGenerationCurrent(snapshotToken: string | null, read: GenerationReadResult): boolean;
54
+ //# sourceMappingURL=resource-generation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-generation.d.ts","sourceRoot":"","sources":["../src/resource-generation.ts"],"names":[],"mappings":"AAqGA,0EAA0E;AAC1E,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7E;AAED,MAAM,WAAW,6BAA6B;IAC5C;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAYxB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAA;AAErF;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,oBAAoB,CAAC,CAW/B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAET"}
@@ -0,0 +1,156 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { atomicWriteFile } from './utils/atomic-write.js';
5
+ import { getErrorMessage, isNodeError } from './utils/error.js';
6
+ import { createDebugLogger } from './utils/debug.js';
7
+ /**
8
+ * Generalized cross-process generation marker for any on-disk resource that
9
+ * is durably cached under a branch-clone (or similar) root.
10
+ *
11
+ * Some in-process cache is kept per resource per process (an in-memory index,
12
+ * a parsed schema, a registry snapshot). Within one process, mutating code can
13
+ * invalidate that cache directly. Across processes (several warm Lambda
14
+ * containers + the EC2 worker sharing branch clones on EFS) there is no
15
+ * shared memory and no cross-host file watching, so the shared filesystem
16
+ * itself is the coordination medium: every operation that mutates the
17
+ * resource also rewrites a small marker file with a fresh random token, and
18
+ * every consumer cheaply re-reads that marker (throttled) to decide whether
19
+ * its cached snapshot is still current.
20
+ *
21
+ * The marker for a given `resource` lives at {root}/.canopy-meta/{resource}.generation:
22
+ * `.canopy-meta/` is the established per-clone internal dir — dot-prefixed (so
23
+ * content and index scans skip it) and excluded from git via .git/info/exclude.
24
+ *
25
+ * ## Why a random token instead of a monotonic counter
26
+ *
27
+ * Readers only need "did it change since I captured it", so inequality against
28
+ * the captured value suffices. A counter would need read-modify-write, which
29
+ * silently loses concurrent bumps without a lock (two bumpers read 5, both
30
+ * write 6 — a reader that recorded 6 after the first bump never learns of the
31
+ * second mutation). A unique token per bump has no lost-update problem, needs
32
+ * no lock, and avoids NFS mtime-granularity / cross-host clock-skew issues.
33
+ * Each bump is a single atomic temp-file + rename.
34
+ *
35
+ * ## Ordering protocol (correctness)
36
+ *
37
+ * - Bumpers write the marker strictly AFTER their filesystem mutations.
38
+ * - Readers capture the marker token strictly BEFORE scanning/rebuilding the
39
+ * resource, and record it only after the rebuild completes. A bump landing
40
+ * mid-rebuild therefore leaves the recorded token older than the file,
41
+ * forcing another rebuild on the next probe.
42
+ *
43
+ * ## Consistency guarantees and residual staleness windows (EFS/NFSv4)
44
+ *
45
+ * Guaranteed: any completed mutation that bumps the marker is observed by
46
+ * every consumer on that root at its next freshness probe. Residual windows
47
+ * (bounded in practice by per-request lifetimes and, where implemented, a
48
+ * suspicious-lookup backstop):
49
+ *
50
+ * - (A) NFS attribute caching (benign direction): another host may not see a
51
+ * new marker for up to the attribute-cache timeout (~3-60s on default EFS
52
+ * mounts; `noac` cannot be assumed). The reader acts on a stale token and
53
+ * keeps a stale snapshot until the cache expires.
54
+ * - (B) Probe throttle: however long the consumer debounces freshness checks.
55
+ * - (C) Self-adoption: after its own mutation a consumer adopts the token it
56
+ * wrote; if a concurrent foreign bump landed just before ours, we miss that
57
+ * one notification (window: from our last token observation to our rename).
58
+ * - (E) Fresh-token/stale-scan (malignant direction, cross-host only): NFS
59
+ * revalidates the marker file on open, but a rebuild's reads may be served
60
+ * from dentry/attribute caches — a rebuild can record a NEW token against
61
+ * PRE-mutation data, leaving that consumer confidently stale until the next
62
+ * bump. Structurally unfixable with a filesystem marker; bounded by
63
+ * per-request lifetimes and any backstop the consumer implements.
64
+ *
65
+ * ## Guarantee delta for DURABLE snapshot consumers (registry / schema-cache)
66
+ *
67
+ * The in-memory ContentIdIndex tolerates window (E) because a stale index is
68
+ * scoped to one process's memory: it self-heals on the next probe or process
69
+ * recycle, and a wrong-file write is independently guarded by ContentStore's
70
+ * existence check. Consumers that persist a durable snapshot ALONGSIDE this
71
+ * marker — a branch registry list, a resolved-schema cache file — do not get
72
+ * that same containment. If such a consumer's regeneration scan is served
73
+ * from stale NFS dentry/attribute caches, window (E) produces a snapshot that
74
+ * embeds a FRESH token over STALE data, and that snapshot is written to disk.
75
+ * Every other host that reads the marker sees the fresh token, concludes the
76
+ * durable snapshot is current, and serves the stale data too — this is now a
77
+ * durable, SHARED staleness visible to all hosts until the next bump, not a
78
+ * transient one-process condition. A random token cannot distinguish "fresh
79
+ * token over fresh data" from "fresh token over stale data" — the token only
80
+ * proves a bump happened, not that the bumping host's own scan observed it.
81
+ *
82
+ * Durable-snapshot consumers must therefore implement mitigations this module
83
+ * cannot provide on their behalf:
84
+ *
85
+ * - Eager regeneration on the mutating host, performed immediately after its
86
+ * own bump (in the same request/operation). That host's own scan is
87
+ * necessarily coherent with the mutation it just made (no NFS round trip
88
+ * was needed to observe its own writes), so regenerating there rather than
89
+ * waiting for a lazy pull on some other host avoids handing window (E) a
90
+ * chance to run at all for the common case.
91
+ * - A get-miss / suspicious-lookup backstop: if a consumer looks up something
92
+ * that "should" exist per the durable snapshot but is missing (or vice
93
+ * versa), that mismatch is a signal to force a fresh regeneration rather
94
+ * than trusting the token match, bounding how long a bad snapshot survives.
95
+ */
96
+ const log = createDebugLogger({ prefix: 'ResourceGeneration' });
97
+ const META_DIR = '.canopy-meta';
98
+ /** Absolute path of the generation marker for `resource` under `root`. */
99
+ export function resourceGenerationPath(root, resource) {
100
+ return path.resolve(root, META_DIR, `${resource}.generation`);
101
+ }
102
+ /**
103
+ * Record on disk that `resource` changed under `root`, so consumers in OTHER
104
+ * processes rebuild. Must be called AFTER the filesystem mutation.
105
+ *
106
+ * Returns the token written, or null if the write failed and `mustSucceed`
107
+ * was not set (logged and swallowed).
108
+ */
109
+ export async function bumpResourceGeneration(root, resource, options) {
110
+ const token = randomUUID();
111
+ try {
112
+ await atomicWriteFile(resourceGenerationPath(root, resource), token);
113
+ return token;
114
+ }
115
+ catch (err) {
116
+ log.warn('generation', `Failed to bump ${resource} generation for ${root}`, {
117
+ error: getErrorMessage(err),
118
+ });
119
+ if (options?.mustSucceed)
120
+ throw err;
121
+ return null;
122
+ }
123
+ }
124
+ /**
125
+ * Read the current generation token for `resource` under `root`.
126
+ *
127
+ * ENOENT maps to `{ok: true, token: null}` — a valid "never bumped" state,
128
+ * distinct from every real token. Any OTHER read error maps to `{ok: false}`
129
+ * (logged) so callers can force-regenerate instead of trusting a null they
130
+ * cannot actually attribute to "never bumped".
131
+ */
132
+ export async function readResourceGeneration(root, resource) {
133
+ try {
134
+ const token = await fs.readFile(resourceGenerationPath(root, resource), 'utf-8');
135
+ return { ok: true, token };
136
+ }
137
+ catch (err) {
138
+ if (isNodeError(err) && err.code === 'ENOENT')
139
+ return { ok: true, token: null };
140
+ log.warn('generation', `Failed to read ${resource} generation for ${root}`, {
141
+ error: getErrorMessage(err),
142
+ });
143
+ return { ok: false };
144
+ }
145
+ }
146
+ /**
147
+ * True when a previously-captured snapshot token is still current: the read
148
+ * succeeded AND its token matches the snapshot's. A failed read (`ok: false`)
149
+ * is never current, even if the snapshot's token happens to be null — an
150
+ * unreadable marker means we cannot vouch for freshness either way, and the
151
+ * safe default is to treat it as stale.
152
+ */
153
+ export function isGenerationCurrent(snapshotToken, read) {
154
+ return read.ok && read.token === snapshotToken;
155
+ }
156
+ //# sourceMappingURL=resource-generation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-generation.js","sourceRoot":"","sources":["../src/resource-generation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACjC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwFG;AAEH,MAAM,GAAG,GAAG,iBAAiB,CAAC,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC,CAAA;AAE/D,MAAM,QAAQ,GAAG,cAAc,CAAA;AAE/B,0EAA0E;AAC1E,MAAM,UAAU,sBAAsB,CAAC,IAAY,EAAE,QAAgB;IACnE,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,QAAQ,aAAa,CAAC,CAAA;AAC/D,CAAC;AAeD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAY,EACZ,QAAgB,EAChB,OAAuC;IAEvC,MAAM,KAAK,GAAG,UAAU,EAAE,CAAA;IAC1B,IAAI,CAAC;QACH,MAAM,eAAe,CAAC,sBAAsB,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAA;QACpE,OAAO,KAAK,CAAA;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,kBAAkB,QAAQ,mBAAmB,IAAI,EAAE,EAAE;YAC1E,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC;SAC5B,CAAC,CAAA;QACF,IAAI,OAAO,EAAE,WAAW;YAAE,MAAM,GAAG,CAAA;QACnC,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAYD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAY,EACZ,QAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAA;QAChF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;IAC5B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;QAC/E,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,kBAAkB,QAAQ,mBAAmB,IAAI,EAAE,EAAE;YAC1E,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC;SAC5B,CAAC,CAAA;QACF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAA;IACtB,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,aAA4B,EAC5B,IAA0B;IAE1B,OAAO,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,KAAK,KAAK,aAAa,CAAA;AAChD,CAAC"}
@@ -160,8 +160,28 @@ export declare class SchemaOps {
160
160
  private readonly services?;
161
161
  constructor(contentRoot: string, entrySchemaRegistry: EntrySchemaRegistry, services?: CanopyServices | undefined);
162
162
  /**
163
- * Invalidate schema cache for this branch after mutations.
164
- * This marks the cache as stale so the next schema load will regenerate it.
163
+ * Invalidate schema cache for this branch after mutations, then eagerly
164
+ * re-resolve on THIS host.
165
+ *
166
+ * The eager re-resolve is the durable-snapshot window-E mitigation (see
167
+ * BranchSchemaCache's class docs): the mutating host's own scan is
168
+ * necessarily coherent with the mutation it just made, whereas the
169
+ * editor's follow-up schema read is a separate Lambda invocation with no
170
+ * container affinity — exactly the lazy foreign-host pull whose scan can
171
+ * be served from stale NFS caches and durably persist a fresh-token
172
+ * snapshot of pre-mutation schema. Regen failures are logged and
173
+ * swallowed (mirroring BranchRegistry.invalidate()): the bump alone
174
+ * already restored correctness for every future reader, and a mutation
175
+ * that leaves no valid schema behind (e.g. deleting the last collection)
176
+ * must not fail the request over an uncacheable resolve.
177
+ *
178
+ * Uses `resolveAndPersist()`, NOT `getSchema()`: getSchema()'s cache-read
179
+ * fast path can return a snapshot a DIFFERENT, concurrent host just wrote
180
+ * (its own eager re-resolve, raced against this one, embedding the
181
+ * now-current marker token over ITS OWN stale-NFS-cache scan) — silently
182
+ * skipping the one scan this call exists to guarantee. resolveAndPersist()
183
+ * never reads the cache file, so it cannot be short-circuited that way.
184
+ * See BranchSchemaCache.resolveAndPersist()'s doc comment for the full race.
165
185
  */
166
186
  private invalidateSchemaCache;
167
187
  /**
@@ -171,6 +191,13 @@ export declare class SchemaOps {
171
191
  * indexes for this branch: in-process via the registry and cross-process via
172
192
  * the on-disk generation marker. ContentStores (and the marker) are rooted
173
193
  * at the branch root, the contentRoot's parent.
194
+ *
195
+ * Uses the combined invalidateBranchContentCaches() helper rather than the
196
+ * content-index-only invalidateContentIndexesDurable(): every call site here
197
+ * is already followed by its own invalidateSchemaCache() call below, so this
198
+ * double-bumps the schema generation marker. That's harmless (bumps are
199
+ * idempotent hints, not counters) and keeps this call uniform with the other
200
+ * bulk-mutation call sites that use the combined helper.
174
201
  */
175
202
  private invalidateContentIdIndexes;
176
203
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"schema-store.d.ts","sourceRoot":"","sources":["../../src/schema/schema-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema-store.d.ts","sourceRoot":"","sources":["../../src/schema/schema-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAMvB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAKlD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,sBAAsB,CAAA;AAG7B,OAAO,KAAK,EACV,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,sBAAsB,CAAA;AAE7B;;GAEG;AACH,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,EAAE,aAAa,CAAA;QACrB,MAAM,EAAE,MAAM,CAAA;QACd,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAC,CAAA;IACF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CACjB;AAED;;GAEG;AACH,UAAU,sBAAsB;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,MAAM,EAAE,aAAa,CAAA;QACrB,MAAM,EAAE,MAAM,CAAA;QACd,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAC,CAAA;IACF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CACjB;AAoBD,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;EAWxB,CAAA;AAEF,QAAA,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAS/B,CAAA;AAEF,QAAA,MAAM,2BAA2B;;;;;;;;;;;;;;;EAgB/B,CAAA;AAEF,QAAA,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;EAM9B,CAAA;AAQF,qBAAa,SAAS;IAElB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAFT,WAAW,EAAE,MAAM,EACnB,mBAAmB,EAAE,mBAAmB,EACxC,QAAQ,CAAC,EAAE,cAAc,YAAA;IAO5C;;;;;;;;;;;;;;;;;;;;;;;OAuBG;YACW,qBAAqB;IAmBnC;;;;;;;;;;;;;;OAcG;YACW,0BAA0B;IAQxC;;OAEG;IACH,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAInD;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAgBhC;;OAEG;IACH,OAAO,CAAC,YAAY;IAgBpB;;OAEG;IACG,kBAAkB,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAmBzF;;OAEG;IACG,sBAAsB,IAAI,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC;IAatE;;OAEG;IACG,iBAAiB,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAqCtE;;OAEG;YACW,mBAAmB;IAMjC;;OAEG;YACW,uBAAuB;IAUrC;;OAEG;IACG,gBAAgB,CACpB,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC;QAAE,cAAc,EAAE,WAAW,CAAC;QAAC,SAAS,EAAE,SAAS,CAAA;KAAE,CAAC;IAoFjE;;OAEG;IACG,gBAAgB,CACpB,cAAc,EAAE,WAAW,EAC3B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC;IA2HhB;;OAEG;IACG,gBAAgB,CAAC,cAAc,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBlE;;OAEG;IACG,YAAY,CAAC,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgD/F;;OAEG;IACG,eAAe,CACnB,cAAc,EAAE,WAAW,EAC3B,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,IAAI,CAAC;IAuDhB;;OAEG;IACG,eAAe,CAAC,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiDxF;;;;;;;;;;;;;;;OAeG;IACG,qBAAqB,CAAC,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsDhG;;OAEG;IACG,WAAW,CAAC,cAAc,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAoB/E;AAMD,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,EAC3B,oBAAoB,EACpB,0BAA0B,GAC3B,CAAA"}