@crowdedkingdoms/crowdyjs 16.2.0 → 17.0.1-dev.1

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 (47) hide show
  1. package/MIGRATION.md +79 -0
  2. package/README.md +1 -1
  3. package/dist/crowdy-dsh/bridge.d.ts +1 -0
  4. package/dist/crowdy-dsh/bridge.d.ts.map +1 -1
  5. package/dist/crowdy-dsh/bridge.js +37 -12
  6. package/dist/crowdy-dsh/protocol.d.ts +25 -5
  7. package/dist/crowdy-dsh/protocol.d.ts.map +1 -1
  8. package/dist/crowdy-dsh/protocol.js +9 -1
  9. package/dist/crowdy-studio/controller.d.ts +22 -16
  10. package/dist/crowdy-studio/controller.d.ts.map +1 -1
  11. package/dist/crowdy-studio/controller.js +55 -72
  12. package/dist/crowdy-studio/dom-shell.d.ts +1 -2
  13. package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
  14. package/dist/crowdy-studio/dom-shell.js +35 -29
  15. package/dist/crowdy-studio/embed/panel.d.ts +10 -0
  16. package/dist/crowdy-studio/embed/panel.d.ts.map +1 -1
  17. package/dist/crowdy-studio/embed/panel.js +3 -1
  18. package/dist/crowdy-studio/github/index.d.ts +2 -2
  19. package/dist/crowdy-studio/github/index.d.ts.map +1 -1
  20. package/dist/crowdy-studio/github/index.js +1 -1
  21. package/dist/crowdy-studio/github/layout.d.ts +22 -0
  22. package/dist/crowdy-studio/github/layout.d.ts.map +1 -0
  23. package/dist/crowdy-studio/github/layout.js +66 -0
  24. package/dist/crowdy-studio/github/transport.d.ts +67 -9
  25. package/dist/crowdy-studio/github/transport.d.ts.map +1 -1
  26. package/dist/crowdy-studio/github/transport.js +61 -17
  27. package/dist/crowdy-studio/models.d.ts +29 -0
  28. package/dist/crowdy-studio/models.d.ts.map +1 -1
  29. package/dist/crowdy-studio/models.js +1 -0
  30. package/dist/default-origin.d.ts +6 -6
  31. package/dist/default-origin.d.ts.map +1 -1
  32. package/dist/default-origin.js +6 -6
  33. package/dist/domains/crowdyStudio.d.ts +25 -3
  34. package/dist/domains/crowdyStudio.d.ts.map +1 -1
  35. package/dist/domains/crowdyStudio.js +183 -10
  36. package/dist/generated/graphql.d.ts +148 -30
  37. package/dist/generated/graphql.d.ts.map +1 -1
  38. package/dist/generated/graphql.js +25 -9
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +1 -1
  42. package/dist/kit/npcs.d.ts.map +1 -1
  43. package/dist/kit/social.d.ts.map +1 -1
  44. package/package.json +1 -1
  45. package/dist/crowdy-studio/github/sync.d.ts +0 -49
  46. package/dist/crowdy-studio/github/sync.d.ts.map +0 -1
  47. package/dist/crowdy-studio/github/sync.js +0 -200
@@ -1,17 +1,29 @@
1
+ import { CrowdyStudioGitHubTransport, } from '../crowdy-studio/github/transport.js';
2
+ import { studioFileToRepoPath } from '../crowdy-studio/github/layout.js';
1
3
  import { CrowdyGraphQLError, CrowdyHttpError, CrowdyNetworkError, CrowdyTimeoutError, } from '../errors.js';
2
4
  import { CrowdyStudioOfflineError, CrowdyStudioRevisionConflictError, normalizeCrowdyStudioPath, } from '../crowdy-studio/models.js';
3
- import { CrowdyStudioCommonFilesDocument, CrowdyStudioImportSource, CrowdyStudioLibraryFilesDocument, CrowdyStudioLibrarySaveDocument, CrowdyStudioPairingPreference as CrowdyStudioPairingPreferenceEnum, CrowdyStudioProjectCreateDocument, CrowdyStudioProjectDocument, CrowdyStudioProjectImportFileDocument, CrowdyStudioProjectSaveDocument, CrowdyStudioProjectsDocument, } from '../generated/graphql.js';
5
+ import { CrowdyStudioCommonFilesDocument, CrowdyStudioImportSource, CrowdyStudioLibraryFilesDocument, CrowdyStudioLibrarySaveDocument, CrowdyStudioPairingPreference as CrowdyStudioPairingPreferenceEnum, CrowdyStudioProjectSource as CrowdyStudioProjectSourceEnum, CrowdyStudioProjectCreateDocument, CrowdyStudioProjectDocument, CrowdyStudioProjectImportFileDocument, CrowdyStudioProjectSaveDocument, CrowdyStudioProjectsDocument, } from '../generated/graphql.js';
4
6
  /**
5
7
  * Schema-coupled Game API adapter for private Crowdy Studio projects and
6
8
  * reusable files. Generated GraphQL documents and DTOs stay in this module;
7
- * the controller and public project models remain transport-neutral. Mutable
8
- * projects remain separate from immutable player-compute versions, and only
9
- * the controller's deploy path converts target files to sourceFilesJson.
9
+ * the controller and public project models remain transport-neutral.
10
+ *
11
+ * A project's `source` decides how {@link saveProject} persists files:
12
+ * `STUDIO` projects go through `crowdyStudioProjectSave` under the project
13
+ * revision; `GITHUB` projects commit each changed file through
14
+ * `crowdyStudioGitHubPutFile` / `DeleteFile` under `github.sha`
15
+ * (`expectedCommitSha`), because their files are a server-maintained mirror
16
+ * of the repository and the Studio file mutations refuse them. Either way a
17
+ * lost race is a {@link CrowdyStudioRevisionConflictError} and the editor's
18
+ * "the remote moved" recovery applies. The controller never learns which.
10
19
  */
11
20
  export class CrowdyStudioAPI {
12
21
  constructor(graphql) {
13
22
  this.graphql = graphql;
14
23
  this.baselines = new Map();
24
+ /** Layout per `projectId@commit`; commits are immutable so this never goes stale. */
25
+ this.layouts = new Map();
26
+ this.github = new CrowdyStudioGitHubTransport(graphql);
15
27
  }
16
28
  async listProjects(scope) {
17
29
  const data = await this.request(CrowdyStudioProjectsDocument, {
@@ -47,13 +59,16 @@ export class CrowdyStudioAPI {
47
59
  return this.remember(fromProjectDto(data.crowdyStudioProjectCreate, input.gridId));
48
60
  }
49
61
  async saveProject(input) {
62
+ const baseline = this.baselines.get(input.projectId) ??
63
+ (await this.getProject({
64
+ appId: input.appId,
65
+ gridId: input.gridId,
66
+ projectId: input.projectId,
67
+ }));
68
+ if (baseline.source === 'GITHUB' && baseline.github?.sha) {
69
+ return this.saveBoundProject(baseline, input);
70
+ }
50
71
  try {
51
- const baseline = this.baselines.get(input.projectId) ??
52
- (await this.getProject({
53
- appId: input.appId,
54
- gridId: input.gridId,
55
- projectId: input.projectId,
56
- }));
57
72
  const delta = projectFileDelta(baseline, input);
58
73
  const data = await this.request(CrowdyStudioProjectSaveDocument, {
59
74
  input: {
@@ -104,6 +119,133 @@ export class CrowdyStudioAPI {
104
119
  throw error;
105
120
  }
106
121
  }
122
+ /**
123
+ * Persist a GITHUB project: metadata through the project save (a CAS on the
124
+ * revision, no file bodies), then each changed file as its own commit on the
125
+ * bound branch, each carrying the commit the previous one produced. A file
126
+ * put is atomic on the server (GitHub commit, then mirror + `github_sha`
127
+ * advance in one transaction), so a stale race part-way through leaves the
128
+ * earlier commits on the branch and this project describing them; the
129
+ * conflict recovery re-reads and the remaining diffs are re-applied against
130
+ * the new commit. Layout comes from the API at the commit being written to.
131
+ */
132
+ async saveBoundProject(baseline, input) {
133
+ const scope = { appId: input.appId, projectId: input.projectId };
134
+ const startSha = baseline.github?.sha;
135
+ if (!startSha) {
136
+ throw new Error('The bound project has no mirror commit; refresh it from GitHub first.');
137
+ }
138
+ // The caller's precondition is the revision IT read. The baseline is the
139
+ // project this provider last returned; the two agree whenever the caller
140
+ // saved or re-read through here. A caller holding an older revision must
141
+ // not ride the baseline's commit onto the branch — that is exactly the
142
+ // lost update the STUDIO path's server-side CAS refuses.
143
+ if (input.expectedRevisionId !== baseline.revision.id) {
144
+ let remoteProject;
145
+ try {
146
+ remoteProject = await this.getProject({
147
+ appId: input.appId,
148
+ gridId: input.gridId,
149
+ projectId: input.projectId,
150
+ });
151
+ }
152
+ catch {
153
+ // The conflict remains actionable if the follow-up read fails.
154
+ }
155
+ throw new CrowdyStudioRevisionConflictError(`CROWDY_STUDIO_REVISION_CONFLICT: expected project revision ${input.expectedRevisionId}; current revision is ${baseline.revision.id}.`, remoteProject);
156
+ }
157
+ try {
158
+ if (metadataChanged(baseline, input)) {
159
+ await this.request(CrowdyStudioProjectSaveDocument, {
160
+ input: {
161
+ appId: input.appId,
162
+ projectId: input.projectId,
163
+ expectedRevision: input.expectedRevisionId,
164
+ gridId: input.gridId,
165
+ name: input.metadata.name,
166
+ description: input.metadata.description ?? null,
167
+ serverModuleName: input.metadata.serverModuleName ?? null,
168
+ clientModuleName: input.metadata.clientModuleName ?? null,
169
+ pairingPreference: toApiPairing(projectKind(input), input.metadata.pairingPreference),
170
+ upserts: [],
171
+ deletes: [],
172
+ },
173
+ });
174
+ }
175
+ const delta = projectFileDelta(baseline, input);
176
+ let sha = startSha;
177
+ if (delta.upserts.length > 0 || delta.deletes.length > 0) {
178
+ const layout = await this.layoutAt(scope, sha);
179
+ for (const file of delta.upserts) {
180
+ const path = normalizeCrowdyStudioPath(file.path);
181
+ const repoPath = studioFileToRepoPath(layout, file.target, path);
182
+ if (!repoPath) {
183
+ throw new Error(`The bound repository's crowdy.json has no ${file.target.toLowerCase()} directory, so ${path} has nowhere to go.`);
184
+ }
185
+ const written = await this.github.putFile({
186
+ ...scope,
187
+ path: repoPath,
188
+ content: file.content,
189
+ message: `studio: update ${repoPath}`,
190
+ expectedCommitSha: sha,
191
+ });
192
+ sha = written.commitSha ?? sha;
193
+ }
194
+ for (const file of delta.deletes) {
195
+ const repoPath = studioFileToRepoPath(layout, file.target, normalizeCrowdyStudioPath(file.path));
196
+ if (!repoPath)
197
+ continue;
198
+ const status = await this.github.deleteFile({
199
+ ...scope,
200
+ path: repoPath,
201
+ message: `studio: delete ${repoPath}`,
202
+ expectedCommitSha: sha,
203
+ });
204
+ sha = status.githubSha ?? sha;
205
+ }
206
+ }
207
+ return await this.getProject({
208
+ appId: input.appId,
209
+ gridId: input.gridId,
210
+ projectId: input.projectId,
211
+ });
212
+ }
213
+ catch (error) {
214
+ if (error instanceof CrowdyGraphQLError &&
215
+ (error.code === 'GITHUB_STALE_SHA' ||
216
+ error.code === 'CROWDY_STUDIO_REVISION_CONFLICT' ||
217
+ error.message.includes('GITHUB_STALE_SHA') ||
218
+ error.message.includes('CROWDY_STUDIO_REVISION_CONFLICT'))) {
219
+ let remoteProject;
220
+ try {
221
+ remoteProject = await this.getProject({
222
+ appId: input.appId,
223
+ gridId: input.gridId,
224
+ projectId: input.projectId,
225
+ });
226
+ }
227
+ catch {
228
+ // The conflict remains actionable if the follow-up read fails.
229
+ }
230
+ throw new CrowdyStudioRevisionConflictError(error.message, remoteProject);
231
+ }
232
+ throw error;
233
+ }
234
+ }
235
+ async layoutAt(scope, commitSha) {
236
+ const key = `${scope.projectId}@${commitSha}`;
237
+ const cached = this.layouts.get(key);
238
+ if (cached)
239
+ return cached;
240
+ const layout = await this.github.layout({ ...scope, commitSha });
241
+ if (this.layouts.size > 64) {
242
+ const [oldest] = this.layouts.keys();
243
+ if (oldest !== undefined)
244
+ this.layouts.delete(oldest);
245
+ }
246
+ this.layouts.set(key, layout);
247
+ return layout;
248
+ }
107
249
  async listPersonalLibraryFiles(scope) {
108
250
  const data = await this.request(CrowdyStudioLibraryFilesDocument, {
109
251
  appId: scope.appId,
@@ -172,11 +314,18 @@ export class CrowdyStudioAPI {
172
314
  }
173
315
  }
174
316
  function fromSummaryDto(dto) {
317
+ const bound = dto.source === CrowdyStudioProjectSourceEnum.Github &&
318
+ dto.githubOwner &&
319
+ dto.githubRepo &&
320
+ dto.githubBranch;
175
321
  return {
176
322
  projectId: dto.projectId,
177
323
  name: dto.name,
178
324
  kind: kindFromApi(dto.pairingPreference),
179
325
  revisionId: String(dto.revision),
326
+ source: bound ? 'GITHUB' : 'STUDIO',
327
+ ...(bound ? { github: `${dto.githubOwner}/${dto.githubRepo}@${dto.githubBranch}` } : {}),
328
+ githubSha: bound ? (dto.githubSha ?? null) : null,
180
329
  ...(dto.serverModuleName
181
330
  ? { serverModuleName: dto.serverModuleName }
182
331
  : {}),
@@ -215,6 +364,18 @@ function fromProjectDto(dto, fallbackGridId) {
215
364
  id: String(dto.revision),
216
365
  savedAt: dto.updatedAt,
217
366
  },
367
+ source: dto.source === CrowdyStudioProjectSourceEnum.Github ? 'GITHUB' : 'STUDIO',
368
+ github: dto.source === CrowdyStudioProjectSourceEnum.Github &&
369
+ dto.githubOwner &&
370
+ dto.githubRepo &&
371
+ dto.githubBranch
372
+ ? {
373
+ owner: dto.githubOwner,
374
+ repo: dto.githubRepo,
375
+ branch: dto.githubBranch,
376
+ sha: dto.githubSha ?? null,
377
+ }
378
+ : null,
218
379
  createdAt: dto.createdAt,
219
380
  updatedAt: dto.updatedAt,
220
381
  };
@@ -282,6 +443,17 @@ function projectKind(input) {
282
443
  return 'SERVER';
283
444
  return 'CLIENT';
284
445
  }
446
+ function metadataChanged(baseline, input) {
447
+ const a = baseline.metadata;
448
+ const b = input.metadata;
449
+ return (baseline.gridId !== input.gridId ||
450
+ a.name !== b.name ||
451
+ (a.description ?? '') !== (b.description ?? '') ||
452
+ (a.serverModuleName ?? '') !== (b.serverModuleName ?? '') ||
453
+ (a.clientModuleName ?? '') !== (b.clientModuleName ?? '') ||
454
+ toApiPairing(baseline.kind, a.pairingPreference) !==
455
+ toApiPairing(projectKind(input), b.pairingPreference));
456
+ }
285
457
  function projectFileDelta(baseline, input) {
286
458
  const previous = new Map(baseline.files.map((file) => [
287
459
  `${file.target}:${normalizeCrowdyStudioPath(file.path)}`,
@@ -306,5 +478,6 @@ function cloneProject(project) {
306
478
  metadata: { ...project.metadata },
307
479
  files: project.files.map((file) => ({ ...file })),
308
480
  revision: { ...project.revision },
481
+ github: project.github ? { ...project.github } : null,
309
482
  };
310
483
  }
@@ -1521,6 +1521,8 @@ export type BillingWriteOff = {
1521
1521
  export type BindCrowdyStudioGitHubInput = {
1522
1522
  appId: Scalars['BigInt']['input'];
1523
1523
  branch?: InputMaybe<Scalars['String']['input']>;
1524
+ /** Which side is the truth for the first commit. */
1525
+ initial: CrowdyStudioGitHubBindInitial;
1524
1526
  owner: Scalars['String']['input'];
1525
1527
  projectId: Scalars['String']['input'];
1526
1528
  repo: Scalars['String']['input'];
@@ -3066,13 +3068,38 @@ export declare enum CrowdyStudioFileProvenance {
3066
3068
  /** The file was copied from a specific revision of the owner’s private personal library. */
3067
3069
  Library = "LIBRARY"
3068
3070
  }
3071
+ export type CrowdyStudioGitHubAtCommitInput = {
3072
+ appId: Scalars['BigInt']['input'];
3073
+ /** Full 40-hex commit to read at. Omitted reads the project githubSha (the mirror commit), never the branch head. */
3074
+ commitSha?: InputMaybe<Scalars['String']['input']>;
3075
+ projectId: Scalars['String']['input'];
3076
+ };
3077
+ /** What the first commit of a bind does. The repository and the Studio project cannot both be the truth on day one, so the owner says which one is. */
3078
+ export declare enum CrowdyStudioGitHubBindInitial {
3079
+ /** The Studio project files are committed to the branch (with a crowdy.json when the repository has none). Refused with GITHUB_REPO_HAS_FILES when the repository already carries rust under the layout roots. */
3080
+ PushProject = "PUSH_PROJECT",
3081
+ /** The repository is adopted as it is; the Studio project files are replaced by the rust under the layout roots at branch HEAD. Refused with GITHUB_REPO_EMPTY when there is nothing to take. */
3082
+ TakeRepository = "TAKE_REPOSITORY"
3083
+ }
3069
3084
  export type CrowdyStudioGitHubConnectStart = {
3070
3085
  __typename?: 'CrowdyStudioGitHubConnectStart';
3071
3086
  /** Install URL for this tier's App, carrying a signed state. */
3072
3087
  connectUrl: Scalars['String']['output'];
3073
3088
  };
3089
+ export type CrowdyStudioGitHubDeleteFileInput = {
3090
+ appId: Scalars['BigInt']['input'];
3091
+ /** The project githubSha the writer read; stale is refused with GITHUB_STALE_SHA. */
3092
+ expectedCommitSha: Scalars['String']['input'];
3093
+ message: Scalars['String']['input'];
3094
+ path: Scalars['String']['input'];
3095
+ projectId: Scalars['String']['input'];
3096
+ /** Blob SHA of the file being deleted. Optional; resolved from the tree at expectedCommitSha. */
3097
+ sha?: InputMaybe<Scalars['String']['input']>;
3098
+ };
3074
3099
  export type CrowdyStudioGitHubFile = {
3075
3100
  __typename?: 'CrowdyStudioGitHubFile';
3101
+ /** Commit the file was read at, or the commit a write created. After a write this is the new project githubSha. */
3102
+ commitSha: Maybe<Scalars['String']['output']>;
3076
3103
  /** UTF-8 content. */
3077
3104
  content: Scalars['String']['output'];
3078
3105
  path: Scalars['String']['output'];
@@ -3081,9 +3108,25 @@ export type CrowdyStudioGitHubFile = {
3081
3108
  };
3082
3109
  export type CrowdyStudioGitHubFileInput = {
3083
3110
  appId: Scalars['BigInt']['input'];
3111
+ /** Full 40-hex commit to read at. Omitted reads the project githubSha (the mirror commit), never the branch head. */
3112
+ commitSha?: InputMaybe<Scalars['String']['input']>;
3084
3113
  path: Scalars['String']['input'];
3085
3114
  projectId: Scalars['String']['input'];
3086
3115
  };
3116
+ /** Where the SERVER and CLIENT crates live in the bound repository, resolved from crowdy.json (or inferred) at one commit. The only layout grammar: clients must not parse crowdy.json themselves. */
3117
+ export type CrowdyStudioGitHubLayout = {
3118
+ __typename?: 'CrowdyStudioGitHubLayout';
3119
+ /** Prefix visitors may fetch assets from (no trailing slash). */
3120
+ assets: Scalars['String']['output'];
3121
+ /** Directory of the CLIENT Cargo.toml, or null when server-only. */
3122
+ client: Maybe<Scalars['String']['output']>;
3123
+ /** Commit the layout was resolved at. */
3124
+ commitSha: Scalars['String']['output'];
3125
+ /** A crowdy.json exists at the repository root. False means the layout was inferred from the tree. */
3126
+ fromFile: Scalars['Boolean']['output'];
3127
+ /** Directory of the SERVER Cargo.toml; `.` is the repository root. */
3128
+ server: Scalars['String']['output'];
3129
+ };
3087
3130
  export type CrowdyStudioGitHubProjectInput = {
3088
3131
  appId: Scalars['BigInt']['input'];
3089
3132
  projectId: Scalars['String']['input'];
@@ -3091,10 +3134,12 @@ export type CrowdyStudioGitHubProjectInput = {
3091
3134
  export type CrowdyStudioGitHubPutFileInput = {
3092
3135
  appId: Scalars['BigInt']['input'];
3093
3136
  content: Scalars['String']['input'];
3137
+ /** The project githubSha the writer read. The write is refused with GITHUB_STALE_SHA when the project has moved on, so two editors cannot silently interleave. */
3138
+ expectedCommitSha: Scalars['String']['input'];
3094
3139
  message: Scalars['String']['input'];
3095
3140
  path: Scalars['String']['input'];
3096
3141
  projectId: Scalars['String']['input'];
3097
- /** Required when the file already exists. */
3142
+ /** Blob SHA of the file being replaced. Optional: the server resolves it from the tree at expectedCommitSha, which is the lock that matters. When sent it must agree with that tree. */
3098
3143
  sha?: InputMaybe<Scalars['String']['input']>;
3099
3144
  };
3100
3145
  export type CrowdyStudioGitHubRepo = {
@@ -3112,19 +3157,26 @@ export type CrowdyStudioGitHubStatus = {
3112
3157
  accountLogin: Maybe<Scalars['String']['output']>;
3113
3158
  /** `User` or `Organization`. */
3114
3159
  accountType: Maybe<Scalars['String']['output']>;
3115
- /** Studio autosave also pushes to the bound repository. Off by default; the project owner opts in. */
3116
- autosave: Scalars['Boolean']['output'];
3117
3160
  branch: Maybe<Scalars['String']['output']>;
3118
3161
  /** This tier has a GitHub App registered. */
3119
3162
  configured: Scalars['Boolean']['output'];
3120
3163
  /** The caller has installed the App on a GitHub account. */
3121
3164
  connected: Scalars['Boolean']['output'];
3165
+ /** Commit the project mirror is at. Every bound write presents it as expectedCommitSha; a stale value is refused with GITHUB_STALE_SHA. Null when the project is not bound. */
3166
+ githubSha: Maybe<Scalars['String']['output']>;
3122
3167
  /** Where to send the modder to install or manage the App. */
3123
3168
  installUrl: Maybe<Scalars['String']['output']>;
3124
3169
  /** Bound repository owner, when a project was named. */
3125
3170
  owner: Maybe<Scalars['String']['output']>;
3126
3171
  repo: Maybe<Scalars['String']['output']>;
3127
3172
  };
3173
+ /** Recursive listing of the bound repository at one commit. The commit is the one the paths were read at, so a follow-up file read can pin it. */
3174
+ export type CrowdyStudioGitHubTree = {
3175
+ __typename?: 'CrowdyStudioGitHubTree';
3176
+ /** Commit the tree was read at. */
3177
+ commitSha: Scalars['String']['output'];
3178
+ entries: Array<CrowdyStudioGitHubTreeEntry>;
3179
+ };
3128
3180
  export type CrowdyStudioGitHubTreeEntry = {
3129
3181
  __typename?: 'CrowdyStudioGitHubTreeEntry';
3130
3182
  /** Repo-relative path. */
@@ -3228,8 +3280,16 @@ export type CrowdyStudioProject = {
3228
3280
  description: Maybe<Scalars['String']['output']>;
3229
3281
  /** Total file count across both targets; each target is independently capped at eight. */
3230
3282
  fileCount: Scalars['Int']['output'];
3231
- /** Private project files ordered by target and path. Bounded to eight files per target. */
3283
+ /** Private project files ordered by target and path. Bounded to eight files per target. For a GITHUB project this is the mirror of the repository at githubSha, read the same way. */
3232
3284
  files: Array<CrowdyStudioProjectFile>;
3285
+ /** Bound branch. */
3286
+ githubBranch: Maybe<Scalars['String']['output']>;
3287
+ /** Bound repository owner, or null for a STUDIO project. */
3288
+ githubOwner: Maybe<Scalars['String']['output']>;
3289
+ /** Bound repository name. */
3290
+ githubRepo: Maybe<Scalars['String']['output']>;
3291
+ /** Commit the mirror is at. Present it as expectedCommitSha on every bound write; a stale value is refused with GITHUB_STALE_SHA. */
3292
+ githubSha: Maybe<Scalars['String']['output']>;
3233
3293
  /** Optional grid affinity. It survives a grid transfer and does not itself grant deploy authority. */
3234
3294
  gridId: Maybe<Scalars['BigInt']['output']>;
3235
3295
  /** Player-facing project name. */
@@ -3246,6 +3306,8 @@ export type CrowdyStudioProject = {
3246
3306
  sdkVersion: Scalars['String']['output'];
3247
3307
  /** Stable crate-style SERVER module name used when the owner chooses to deploy, or null when unassigned. */
3248
3308
  serverModuleName: Maybe<Scalars['String']['output']>;
3309
+ /** STUDIO until the owner binds a repository; GITHUB while one is bound. Decides which mutations may write files. */
3310
+ source: CrowdyStudioProjectSource;
3249
3311
  /** Total UTF-8 source bytes across both targets, bounded by target and aggregate owner quotas. */
3250
3312
  totalBytes: Scalars['BigInt']['output'];
3251
3313
  /** Timestamp of the latest successful project mutation. */
@@ -3291,6 +3353,13 @@ export type CrowdyStudioProjectFileInput = {
3291
3353
  /** Project target receiving this file. */
3292
3354
  target: CrowdyStudioTarget;
3293
3355
  };
3356
+ /** Where a project’s files are authored. Every project starts as STUDIO; its owner may bind a GitHub repository later (crowdyStudioGitHubBind) and unbind it again. GitHub is never required. */
3357
+ export declare enum CrowdyStudioProjectSource {
3358
+ /** The bound repository is the working tree. `files` is a server-maintained mirror of the rust under the layout roots at githubSha; writes go through crowdyStudioGitHubPutFile / DeleteFile with expectedCommitSha, and the Studio file mutations refuse with GITHUB_BOUND_USE_CONTENTS. */
3359
+ Github = "GITHUB",
3360
+ /** Files live in Crowdy Studio and are written with the crowdyStudioProject* file mutations under the project revision. */
3361
+ Studio = "STUDIO"
3362
+ }
3294
3363
  /** Whether this player has accepted that their Crowdy Studio project source may be sent to a model provider (under the platform ZDR / no-collection routing) for this app. */
3295
3364
  export type CrowdyStudioProviderConsent = {
3296
3365
  __typename?: 'CrowdyStudioProviderConsent';
@@ -3359,24 +3428,23 @@ export type DeployComputeVersionInput = {
3359
3428
  /** JSON object mapping relative paths to file contents. Must include Cargo.toml and src/lib.rs; only .rs files under src/ plus Cargo.toml are allowed. Dependencies are restricted to the platform crate allowlist; build.rs and [build-dependencies] are rejected. */
3360
3429
  sourceFilesJson: Scalars['String']['input'];
3361
3430
  };
3431
+ /** Deploy one target of a Crowdy Studio project the caller owns. The server resolves the source itself — the project files at their saved revision for a STUDIO project, the rust under the layout roots at commitSha (default githubSha) for a GITHUB project — so a client never uploads file bodies and what compiles is exactly what was saved or committed. */
3362
3432
  export type DeployPlayerComputeInput = {
3363
- /** Guest ABI version. P1 supports ABI 0. */
3364
- abiVersion?: InputMaybe<Scalars['Int']['input']>;
3365
3433
  /** App containing the owned grid. */
3366
3434
  appId: Scalars['BigInt']['input'];
3435
+ /** GITHUB projects only: the full 40-hex commit to compile. Defaults to the project githubSha (the mirror commit). Refused for a STUDIO project. */
3436
+ commitSha?: InputMaybe<Scalars['String']['input']>;
3367
3437
  /** Optional player-facing module description. */
3368
3438
  description?: InputMaybe<Scalars['String']['input']>;
3369
3439
  /** Deploy in draft mode (live-coding iteration): the module runs for its author but its spatial egress is suppressed server-side, so no other session in the grid observes its world effects. Omit or false for a normal deploy. */
3370
3440
  draft?: InputMaybe<Scalars['Boolean']['input']>;
3371
3441
  /** Owned grid that confines server execution. */
3372
3442
  gridId: Scalars['BigInt']['input'];
3373
- /** Lowercase crate-style module name, unique within the grid. */
3374
- name: Scalars['String']['input'];
3375
- /** Pinned crowdy-compute-sdk version. Defaults to 0.1.5. */
3376
- sdkVersion?: InputMaybe<Scalars['String']['input']>;
3377
- /** JSON object mapping Cargo.toml and src/*.rs relative paths to source. P1 player caps: at most 8 files, 64 KiB/file, 256 KiB total. */
3378
- sourceFilesJson: Scalars['String']['input'];
3379
- /** SERVER or CLIENT compile target. */
3443
+ /** Lowercase crate-style module name, unique within the grid. Defaults to the project’s module name for the target and must equal it when both are set. */
3444
+ name?: InputMaybe<Scalars['String']['input']>;
3445
+ /** Crowdy Studio project (owned by the caller, in this app) whose target is deployed. */
3446
+ projectId: Scalars['String']['input'];
3447
+ /** SERVER or CLIENT compile target; the project must have files for it. */
3380
3448
  target: PlayerComputeTarget;
3381
3449
  /** Optional server tick rate in Hz. Omit for invoke/event-only modules or CLIENT artifacts. Clamped by the effective player/app policy. */
3382
3450
  tickHz?: InputMaybe<Scalars['Float']['input']>;
@@ -5071,15 +5139,17 @@ export type Mutation = {
5071
5139
  creditOrgWallet: WalletTransaction;
5072
5140
  /** Publish a new immutable version of an app-scoped Crowdy Studio-curated common file and make it the current player-readable version. Requires an app-scoped token plus the app manage_compute permission. Old versions remain immutable for provenance; an idempotency key is strongly recommended for transport retries. */
5073
5141
  crowdyStudioCommonPublish: CrowdyStudioCommonFile;
5074
- /** Bind a Crowdy Studio project you own to a repository granted to your installation. Verifies the branch exists. Identity session only. Autosave stays off until you turn it on. */
5142
+ /** Bind a Crowdy Studio project you own to a repository granted to your installation, and make the two agree in one commit: PUSH_PROJECT commits the project files to the branch (refused with GITHUB_REPO_HAS_FILES when the branch already has rust under the layout roots); TAKE_REPOSITORY replaces the project files with the rust on the branch (refused with GITHUB_REPO_EMPTY when there is none). From then on the repository is the working tree and the project files are a mirror of it at githubSha. Identity session only. */
5075
5143
  crowdyStudioGitHubBind: CrowdyStudioGitHubStatus;
5076
5144
  /** Install URL for this tier's Crowdy Studio GitHub App with a signed state. Identity session only. No GitHub token ever reaches the browser. */
5077
5145
  crowdyStudioGitHubConnectUrl: CrowdyStudioGitHubConnectStart;
5078
- /** Create or update one UTF-8 file in the bound repository. Send the current sha on update; a stale sha is refused with GITHUB_STALE_SHA. */
5146
+ /** Delete one file from the bound repository as a commit on the bound branch, under the same blob-sha and expectedCommitSha guards as crowdyStudioGitHubPutFile. A project file leaves the mirror with it. crowdy.json cannot be deleted this way. */
5147
+ crowdyStudioGitHubDeleteFile: CrowdyStudioGitHubStatus;
5148
+ /** Create or update one UTF-8 file in the bound repository as a commit on the bound branch. Send the blob sha on update and the project githubSha as expectedCommitSha; either being stale is refused with GITHUB_STALE_SHA and nothing moves. When the path is a project file under the layout roots the project mirror advances with it; the returned commitSha is the new githubSha. */
5079
5149
  crowdyStudioGitHubPutFile: CrowdyStudioGitHubFile;
5080
- /** Opt a bound project into (or out of) pushing Studio autosaves to GitHub. Default is off. */
5081
- crowdyStudioGitHubSetAutosave: CrowdyStudioGitHubStatus;
5082
- /** Remove the repository bind from a project you own. Files in Crowdy Studio are untouched. */
5150
+ /** Bring the project mirror forward to the branch head. This is how a push made outside Crowdy Studio reaches the editor and the agent; githubSha becomes the head commit. A no-op when already at head. */
5151
+ crowdyStudioGitHubRefresh: CrowdyStudioGitHubStatus;
5152
+ /** Remove the repository bind from a project you own. The project keeps its files (the mirror at githubSha) and is a plain Crowdy Studio project again; nothing on GitHub changes. Identity session only. */
5083
5153
  crowdyStudioGitHubUnbind: CrowdyStudioGitHubStatus;
5084
5154
  /** Create or optimistically update one private personal-library source file. Requires an app-scoped token; ownership is always the authenticated player and cannot be delegated. Safe source paths, 64-KiB content, revision, and configurable bounded aggregate library storage are enforced atomically. */
5085
5155
  crowdyStudioLibrarySave: CrowdyStudioLibraryFile;
@@ -5619,11 +5689,14 @@ export type MutationCrowdyStudioCommonPublishArgs = {
5619
5689
  export type MutationCrowdyStudioGitHubBindArgs = {
5620
5690
  input: BindCrowdyStudioGitHubInput;
5621
5691
  };
5692
+ export type MutationCrowdyStudioGitHubDeleteFileArgs = {
5693
+ input: CrowdyStudioGitHubDeleteFileInput;
5694
+ };
5622
5695
  export type MutationCrowdyStudioGitHubPutFileArgs = {
5623
5696
  input: CrowdyStudioGitHubPutFileInput;
5624
5697
  };
5625
- export type MutationCrowdyStudioGitHubSetAutosaveArgs = {
5626
- input: SetCrowdyStudioGitHubAutosaveInput;
5698
+ export type MutationCrowdyStudioGitHubRefreshArgs = {
5699
+ input: CrowdyStudioGitHubProjectInput;
5627
5700
  };
5628
5701
  export type MutationCrowdyStudioGitHubUnbindArgs = {
5629
5702
  input: CrowdyStudioGitHubProjectInput;
@@ -7294,12 +7367,18 @@ export type PlayerWasmModuleVersion = {
7294
7367
  compiledSizeBytes: Maybe<Scalars['BigInt']['output']>;
7295
7368
  /** Version creation time. */
7296
7369
  createdAt: Scalars['DateTime']['output'];
7370
+ /** Commit the rust was fetched at (GITHUB projects). A force-push of the bound branch never changes what this version runs. */
7371
+ githubCommitSha: Maybe<Scalars['String']['output']>;
7297
7372
  /** Parent module UUID. */
7298
7373
  moduleId: Scalars['String']['output'];
7299
7374
  /** Whether anyone may read this immutable version source. */
7300
7375
  openSource: Scalars['Boolean']['output'];
7376
+ /** Crowdy Studio project this version was deployed from, when known. */
7377
+ projectId: Maybe<Scalars['String']['output']>;
7301
7378
  /** JSON source map. Null for non-authors of closed-source code; P1 exposes no moderation override. */
7302
7379
  sourceFilesJson: Maybe<Scalars['String']['output']>;
7380
+ /** Project revision the source was read at (STUDIO projects). */
7381
+ sourceRevision: Maybe<Scalars['BigInt']['output']>;
7303
7382
  /** Compile target. */
7304
7383
  target: PlayerComputeTarget;
7305
7384
  /** Version UUID. */
@@ -7606,14 +7685,16 @@ export type Query = {
7606
7685
  crowdyStudioAgentUsage: CrowdyStudioAgentUsagePage;
7607
7686
  /** List the current immutable versions of published Crowdy Studio-curated common files for one app. Requires an app-scoped token for appId; unlike private player source, this catalog content is intentionally readable by players in that app. Results are bounded and may be filtered by target. */
7608
7687
  crowdyStudioCommonFiles: Array<CrowdyStudioCommonFile>;
7609
- /** One UTF-8 file from the bound repository, with its blob SHA. */
7688
+ /** One UTF-8 file from the bound repository at one commit (default: githubSha), with its blob SHA. */
7610
7689
  crowdyStudioGitHubFile: CrowdyStudioGitHubFile;
7690
+ /** Where the SERVER and CLIENT crates live in the bound repository, resolved from crowdy.json (or inferred from the tree) at one commit. The only layout grammar: clients must not parse crowdy.json themselves. */
7691
+ crowdyStudioGitHubLayout: CrowdyStudioGitHubLayout;
7611
7692
  /** Repositories the caller granted to their Crowdy Studio installation. Identity session only. */
7612
7693
  crowdyStudioGitHubRepos: Array<CrowdyStudioGitHubRepo>;
7613
- /** GitHub connection for the caller and the repository bound to a Crowdy Studio project. GitHub is a Studio filesystem capability, not a login provider. */
7694
+ /** GitHub connection for the caller and the repository bound to a Crowdy Studio project, with the commit the project mirror is at (githubSha). GitHub is a Studio filesystem capability, not a login provider, and it is never required: a project starts in Crowdy Studio and may be bound later. */
7614
7695
  crowdyStudioGitHubStatus: CrowdyStudioGitHubStatus;
7615
- /** Recursive file list of the repository bound to the project (Git Trees API). */
7616
- crowdyStudioGitHubTree: Array<CrowdyStudioGitHubTreeEntry>;
7696
+ /** Recursive file list of the repository bound to the project at one commit (default: githubSha, the mirror commit). */
7697
+ crowdyStudioGitHubTree: CrowdyStudioGitHubTree;
7617
7698
  /** List the authenticated player’s private reusable Crowdy Studio source files in one app. Requires an app-scoped token for appId; cross-user entries are never visible, including to grid owners or Crowdy Studio operators. The bounded result defaults to 50. */
7618
7699
  crowdyStudioLibraryFiles: Array<CrowdyStudioLibraryFile>;
7619
7700
  /** The caller's own model usage in this app: today's request count and rate-carded charge against the policy ceiling, the payer (PLAYER wallet by default, ORG when the app elected it, PLATFORM while platform-funded), and the most recent requests. Counts and charges only; no prompts or provider bodies exist to return. Requires 'use_studio_agent'. */
@@ -8143,12 +8224,15 @@ export type QueryCrowdyStudioCommonFilesArgs = {
8143
8224
  export type QueryCrowdyStudioGitHubFileArgs = {
8144
8225
  input: CrowdyStudioGitHubFileInput;
8145
8226
  };
8227
+ export type QueryCrowdyStudioGitHubLayoutArgs = {
8228
+ input: CrowdyStudioGitHubAtCommitInput;
8229
+ };
8146
8230
  export type QueryCrowdyStudioGitHubStatusArgs = {
8147
8231
  appId?: InputMaybe<Scalars['BigInt']['input']>;
8148
8232
  projectId?: InputMaybe<Scalars['String']['input']>;
8149
8233
  };
8150
8234
  export type QueryCrowdyStudioGitHubTreeArgs = {
8151
- input: CrowdyStudioGitHubProjectInput;
8235
+ input: CrowdyStudioGitHubAtCommitInput;
8152
8236
  };
8153
8237
  export type QueryCrowdyStudioLibraryFilesArgs = {
8154
8238
  appId: Scalars['BigInt']['input'];
@@ -9345,11 +9429,6 @@ export type SetCrowdyStudioAgentPlatformPolicyInput = {
9345
9429
  /** Optional per-turn limit patch. */
9346
9430
  turnLimits?: InputMaybe<CrowdyStudioAgentTurnLimitsInput>;
9347
9431
  };
9348
- export type SetCrowdyStudioGitHubAutosaveInput = {
9349
- appId: Scalars['BigInt']['input'];
9350
- autosave: Scalars['Boolean']['input'];
9351
- projectId: Scalars['String']['input'];
9352
- };
9353
9432
  /** Optimistically archive or restore one caller-owned personal-library file. */
9354
9433
  export type SetCrowdyStudioLibraryFileArchivedInput = {
9355
9434
  /** App tenant that owns the private library entry. */
@@ -13038,6 +13117,11 @@ export type CrowdyStudioProjectFieldsFragment = {
13038
13117
  archivedAt: string | null;
13039
13118
  fileCount: number;
13040
13119
  totalBytes: string;
13120
+ source: CrowdyStudioProjectSource;
13121
+ githubOwner: string | null;
13122
+ githubRepo: string | null;
13123
+ githubBranch: string | null;
13124
+ githubSha: string | null;
13041
13125
  createdAt: string;
13042
13126
  updatedAt: string;
13043
13127
  files: Array<{
@@ -13072,6 +13156,11 @@ export type CrowdyStudioProjectsQuery = {
13072
13156
  pairingPreference: CrowdyStudioPairingPreference;
13073
13157
  revision: string;
13074
13158
  archived: boolean;
13159
+ source: CrowdyStudioProjectSource;
13160
+ githubOwner: string | null;
13161
+ githubRepo: string | null;
13162
+ githubBranch: string | null;
13163
+ githubSha: string | null;
13075
13164
  updatedAt: string;
13076
13165
  }>;
13077
13166
  };
@@ -13099,6 +13188,11 @@ export type CrowdyStudioProjectQuery = {
13099
13188
  archivedAt: string | null;
13100
13189
  fileCount: number;
13101
13190
  totalBytes: string;
13191
+ source: CrowdyStudioProjectSource;
13192
+ githubOwner: string | null;
13193
+ githubRepo: string | null;
13194
+ githubBranch: string | null;
13195
+ githubSha: string | null;
13102
13196
  createdAt: string;
13103
13197
  updatedAt: string;
13104
13198
  files: Array<{
@@ -13139,6 +13233,11 @@ export type CrowdyStudioProjectCreateMutation = {
13139
13233
  archivedAt: string | null;
13140
13234
  fileCount: number;
13141
13235
  totalBytes: string;
13236
+ source: CrowdyStudioProjectSource;
13237
+ githubOwner: string | null;
13238
+ githubRepo: string | null;
13239
+ githubBranch: string | null;
13240
+ githubSha: string | null;
13142
13241
  createdAt: string;
13143
13242
  updatedAt: string;
13144
13243
  files: Array<{
@@ -13179,6 +13278,11 @@ export type CrowdyStudioProjectSaveMutation = {
13179
13278
  archivedAt: string | null;
13180
13279
  fileCount: number;
13181
13280
  totalBytes: string;
13281
+ source: CrowdyStudioProjectSource;
13282
+ githubOwner: string | null;
13283
+ githubRepo: string | null;
13284
+ githubBranch: string | null;
13285
+ githubSha: string | null;
13182
13286
  createdAt: string;
13183
13287
  updatedAt: string;
13184
13288
  files: Array<{
@@ -13295,6 +13399,11 @@ export type CrowdyStudioProjectImportFileMutation = {
13295
13399
  archivedAt: string | null;
13296
13400
  fileCount: number;
13297
13401
  totalBytes: string;
13402
+ source: CrowdyStudioProjectSource;
13403
+ githubOwner: string | null;
13404
+ githubRepo: string | null;
13405
+ githubBranch: string | null;
13406
+ githubSha: string | null;
13298
13407
  createdAt: string;
13299
13408
  updatedAt: string;
13300
13409
  files: Array<{
@@ -16378,6 +16487,9 @@ export type PlayerWasmModuleVersionFieldsFragment = {
16378
16487
  compileStatus: string;
16379
16488
  compileLog: string | null;
16380
16489
  compiledSizeBytes: string | null;
16490
+ projectId: string | null;
16491
+ sourceRevision: string | null;
16492
+ githubCommitSha: string | null;
16381
16493
  createdAt: string;
16382
16494
  };
16383
16495
  export type PlayerComputeDeployMutationVariables = Exact<{
@@ -16396,6 +16508,9 @@ export type PlayerComputeDeployMutation = {
16396
16508
  compileStatus: string;
16397
16509
  compileLog: string | null;
16398
16510
  compiledSizeBytes: string | null;
16511
+ projectId: string | null;
16512
+ sourceRevision: string | null;
16513
+ githubCommitSha: string | null;
16399
16514
  createdAt: string;
16400
16515
  };
16401
16516
  };
@@ -16478,6 +16593,9 @@ export type PlayerComputeVersionsQuery = {
16478
16593
  compileStatus: string;
16479
16594
  compileLog: string | null;
16480
16595
  compiledSizeBytes: string | null;
16596
+ projectId: string | null;
16597
+ sourceRevision: string | null;
16598
+ githubCommitSha: string | null;
16481
16599
  createdAt: string;
16482
16600
  }>;
16483
16601
  };