@liforma/publisher 0.5.0 → 0.7.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 (48) hide show
  1. package/README.md +47 -4
  2. package/dist/createPublisher.d.ts +3 -1
  3. package/dist/createPublisher.d.ts.map +1 -1
  4. package/dist/createPublisher.js +2 -0
  5. package/dist/createPublisher.js.map +1 -1
  6. package/dist/errors.d.ts +6 -0
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js +2 -0
  9. package/dist/errors.js.map +1 -1
  10. package/dist/imageSource.d.ts +28 -0
  11. package/dist/imageSource.d.ts.map +1 -0
  12. package/dist/imageSource.js +155 -0
  13. package/dist/imageSource.js.map +1 -0
  14. package/dist/index.d.ts +7 -3
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/publishStart.d.ts +9 -0
  19. package/dist/publishStart.d.ts.map +1 -1
  20. package/dist/publishStart.js +10 -2
  21. package/dist/publishStart.js.map +1 -1
  22. package/dist/resources/characters.d.ts +2 -0
  23. package/dist/resources/characters.d.ts.map +1 -1
  24. package/dist/resources/characters.js +2 -0
  25. package/dist/resources/characters.js.map +1 -1
  26. package/dist/resources/costumes.d.ts +19 -0
  27. package/dist/resources/costumes.d.ts.map +1 -0
  28. package/dist/resources/costumes.js +43 -0
  29. package/dist/resources/costumes.js.map +1 -0
  30. package/dist/resources/createFrom.d.ts +93 -0
  31. package/dist/resources/createFrom.d.ts.map +1 -0
  32. package/dist/resources/createFrom.js +494 -0
  33. package/dist/resources/createFrom.js.map +1 -0
  34. package/dist/resources/experiences.d.ts +6 -1
  35. package/dist/resources/experiences.d.ts.map +1 -1
  36. package/dist/resources/experiences.js +5 -1
  37. package/dist/resources/experiences.js.map +1 -1
  38. package/dist/resources/sets.d.ts +2 -0
  39. package/dist/resources/sets.d.ts.map +1 -1
  40. package/dist/resources/sets.js +2 -0
  41. package/dist/resources/sets.js.map +1 -1
  42. package/dist/schemas.d.ts +369 -20
  43. package/dist/schemas.d.ts.map +1 -1
  44. package/dist/schemas.js +55 -1
  45. package/dist/schemas.js.map +1 -1
  46. package/dist/version.d.ts +1 -1
  47. package/dist/version.js +1 -1
  48. package/package.json +1 -1
@@ -0,0 +1,43 @@
1
+ import { projectPath, publisherRequest } from '../http.js';
2
+ import { waitForJob } from '../jobs.js';
3
+ import { startJobFromEnvelope } from '../publishStart.js';
4
+ import { wardrobePublishBody } from './clothes.js';
5
+ import { createCostumePublishResponseSchema, deletedResourceResponseSchema, getCostumeResponseSchema } from '../schemas.js';
6
+ export function createCostumesClient(http) {
7
+ const get = async (id, options) => {
8
+ const { data } = await publisherRequest(http, projectPath(http, `/costumes/${encodeURIComponent(id)}`), getCostumeResponseSchema, { method: 'GET', signal: options?.signal, retry: true });
9
+ return data.costume;
10
+ };
11
+ const startCreate = async (input, options) => startJobFromEnvelope(http, '/costumes', createCostumePublishResponseSchema, wardrobePublishBody(input), options?.signal);
12
+ return {
13
+ startCreate,
14
+ create: async (input, options = {}) => {
15
+ const started = await startCreate(input, options);
16
+ const completed = await waitForJob(http, started.id, options);
17
+ return get(completed.targetId, options);
18
+ },
19
+ get,
20
+ update: async (id, patch, options) => {
21
+ const { data } = await publisherRequest(http, projectPath(http, `/costumes/${encodeURIComponent(id)}`), getCostumeResponseSchema, {
22
+ method: 'PATCH',
23
+ body: { name: patch.name },
24
+ signal: options?.signal,
25
+ retry: true
26
+ });
27
+ return data.costume;
28
+ },
29
+ archive: async (id, options) => {
30
+ const { data } = await publisherRequest(http, projectPath(http, `/costumes/${encodeURIComponent(id)}/archive`), getCostumeResponseSchema, { method: 'POST', signal: options?.signal });
31
+ return data.costume;
32
+ },
33
+ restore: async (id, options) => {
34
+ const { data } = await publisherRequest(http, projectPath(http, `/costumes/${encodeURIComponent(id)}/restore`), getCostumeResponseSchema, { method: 'POST', signal: options?.signal });
35
+ return data.costume;
36
+ },
37
+ delete: async (id, options) => {
38
+ const { data } = await publisherRequest(http, projectPath(http, `/costumes/${encodeURIComponent(id)}`), deletedResourceResponseSchema, { method: 'DELETE', signal: options?.signal });
39
+ return data;
40
+ }
41
+ };
42
+ }
43
+ //# sourceMappingURL=costumes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"costumes.js","sourceRoot":"","sources":["../../src/resources/costumes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ import { type PublisherHttp } from '../http.js';
2
+ import { type ImageSource } from '../imageSource.js';
3
+ import { type BackdropResource, type CharacterResource, type ClothesResource, type CostumeResource, type ExperienceResource, type ExperienceInteractionInput, type HairResource, type PersonaEthnicity, type PersonaGender, type PublishJob, type SetResource } from '../schemas.js';
4
+ import type { BackgroundMode } from './clothes.js';
5
+ export type PlateCreate = {
6
+ readonly image: ImageSource;
7
+ readonly name?: string;
8
+ readonly backgroundMode?: BackgroundMode;
9
+ readonly externalId?: string;
10
+ readonly forceNew?: boolean;
11
+ };
12
+ export type PlateOrId = {
13
+ readonly id: string;
14
+ } | PlateCreate;
15
+ export type BackdropCreate = {
16
+ readonly image: ImageSource;
17
+ readonly name?: string;
18
+ readonly externalId?: string;
19
+ readonly forceNew?: boolean;
20
+ readonly depth?: {
21
+ readonly image: ImageSource;
22
+ readonly depthMapType?: 'disparity';
23
+ readonly metersPerDepth?: number;
24
+ };
25
+ };
26
+ export type CreateFromBackdropInput = {
27
+ readonly id: string;
28
+ } | BackdropCreate;
29
+ export type CreateFromCharacterCreate = {
30
+ readonly avatarId: string;
31
+ readonly name: string;
32
+ readonly voice: string;
33
+ readonly sttLang: string;
34
+ readonly costume?: PlateOrId;
35
+ readonly clothes?: PlateOrId;
36
+ readonly hair?: PlateOrId;
37
+ readonly personality?: string;
38
+ readonly generalInstructions?: string;
39
+ readonly gender?: PersonaGender;
40
+ readonly age?: number;
41
+ readonly ethnicity?: PersonaEthnicity;
42
+ readonly externalId?: string;
43
+ };
44
+ export type CreateFromCharacterInput = {
45
+ readonly id: string;
46
+ } | CreateFromCharacterCreate;
47
+ export type CreateFromExperienceInput = {
48
+ readonly title: string;
49
+ readonly backdrop: CreateFromBackdropInput;
50
+ readonly character: CreateFromCharacterInput;
51
+ readonly set?: {
52
+ readonly name?: string;
53
+ readonly externalId?: string;
54
+ };
55
+ readonly slug?: string;
56
+ readonly attributes?: Record<string, string>;
57
+ readonly startingMessage?: string;
58
+ readonly systemInstructions?: string;
59
+ readonly introduction?: string;
60
+ readonly interaction?: ExperienceInteractionInput;
61
+ readonly publish?: boolean;
62
+ readonly externalId?: string;
63
+ };
64
+ export type CreateFromStage = 'backdrop' | 'costume' | 'clothes' | 'hair';
65
+ export type CreateFromProgress = {
66
+ readonly stage: CreateFromStage;
67
+ readonly job: PublishJob;
68
+ };
69
+ export type CreateFromOptions = {
70
+ readonly signal?: AbortSignal;
71
+ readonly timeoutMs?: number;
72
+ readonly onProgress?: (progress: CreateFromProgress) => void;
73
+ };
74
+ export type CreateFromResult = {
75
+ readonly experience: ExperienceResource;
76
+ readonly character: CharacterResource;
77
+ readonly set: SetResource;
78
+ readonly backdrop: BackdropResource;
79
+ readonly costume?: CostumeResource;
80
+ readonly clothes?: ClothesResource;
81
+ readonly hair?: HairResource;
82
+ readonly created: {
83
+ readonly experience: boolean;
84
+ readonly character: boolean;
85
+ readonly set: boolean;
86
+ readonly backdrop: boolean;
87
+ readonly costume?: boolean;
88
+ readonly clothes?: boolean;
89
+ readonly hair?: boolean;
90
+ };
91
+ };
92
+ export declare function createExperienceFrom(http: PublisherHttp, input: CreateFromExperienceInput, options?: CreateFromOptions): Promise<CreateFromResult>;
93
+ //# sourceMappingURL=createFrom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createFrom.d.ts","sourceRoot":"","sources":["../../src/resources/createFrom.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,494 @@
1
+ import { LiformaPublisherError } from '../errors.js';
2
+ import { projectPath, publisherRequest } from '../http.js';
3
+ import { resolveImageSource } from '../imageSource.js';
4
+ import { waitForJob } from '../jobs.js';
5
+ import { startJobWithOutcome } from '../publishStart.js';
6
+ import { createBackdropPublishResponseSchema, createCharacterResponseSchema, createClothesPublishResponseSchema, createCostumePublishResponseSchema, createHairPublishResponseSchema, createSetResponseSchema, experienceResponseSchema, getBackdropResponseSchema, getClothesResponseSchema, getCostumeResponseSchema, getHairResponseSchema } from '../schemas.js';
7
+ import { requireInput, requiredString } from '../validate.js';
8
+ import { wardrobePublishBody } from './clothes.js';
9
+ /** Prefer explicit `{ id }` reuse when both id and create fields are present. */
10
+ function reuseId(value) {
11
+ if (!('id' in value))
12
+ return null;
13
+ const id = value.id;
14
+ if (typeof id !== 'string' || !id.trim())
15
+ return null;
16
+ return id.trim();
17
+ }
18
+ function withContext(error, step) {
19
+ if (error instanceof LiformaPublisherError) {
20
+ if (error.context?.operation === 'experiences.createFrom') {
21
+ return error;
22
+ }
23
+ return new LiformaPublisherError(error.message, {
24
+ code: error.code,
25
+ status: error.status,
26
+ requestId: error.requestId,
27
+ details: error.details,
28
+ retryable: error.retryable,
29
+ job: error.job,
30
+ cause: error,
31
+ context: {
32
+ operation: 'experiences.createFrom',
33
+ step
34
+ }
35
+ });
36
+ }
37
+ return new LiformaPublisherError(error instanceof Error ? error.message : 'experiences.createFrom failed.', {
38
+ code: 'UNKNOWN',
39
+ cause: error,
40
+ context: { operation: 'experiences.createFrom', step }
41
+ });
42
+ }
43
+ async function runStep(step, fn) {
44
+ try {
45
+ return await fn();
46
+ }
47
+ catch (error) {
48
+ throw withContext(error, step);
49
+ }
50
+ }
51
+ async function resolveUpload(http, image, options, purpose = 'colour') {
52
+ return runStep('resolveImage', () => resolveImageSource(http, image, { signal: options.signal, purpose }));
53
+ }
54
+ async function waitPlate(http, jobId, stage, options) {
55
+ return waitForJob(http, jobId, {
56
+ signal: options.signal,
57
+ timeoutMs: options.timeoutMs,
58
+ onProgress: options.onProgress
59
+ ? (job) => {
60
+ options.onProgress?.({ stage, job });
61
+ }
62
+ : undefined
63
+ });
64
+ }
65
+ async function getBackdrop(http, id, options) {
66
+ const { data } = await publisherRequest(http, projectPath(http, `/backdrops/${encodeURIComponent(id)}`), getBackdropResponseSchema, { method: 'GET', signal: options.signal, retry: true });
67
+ return data.backdrop;
68
+ }
69
+ async function getSet(http, id, options) {
70
+ const { data } = await publisherRequest(http, projectPath(http, `/sets/${encodeURIComponent(id)}`), createSetResponseSchema, { method: 'GET', signal: options.signal, retry: true });
71
+ return data.set;
72
+ }
73
+ async function getCharacter(http, id, options) {
74
+ const { data } = await publisherRequest(http, projectPath(http, `/characters/${encodeURIComponent(id)}`), createCharacterResponseSchema, { method: 'GET', signal: options.signal, retry: true });
75
+ return data.character;
76
+ }
77
+ async function resolveBackdrop(http, input, options, defaultName) {
78
+ const id = reuseId(input);
79
+ if (id) {
80
+ return runStep('backdrop', async () => ({
81
+ backdrop: await getBackdrop(http, id, options),
82
+ created: false
83
+ }));
84
+ }
85
+ if (!('image' in input)) {
86
+ throw new LiformaPublisherError('backdrop must be { id } or include image.', { code: 'INVALID_BODY' });
87
+ }
88
+ const create = input;
89
+ return runStep('backdrop', async () => {
90
+ const image = await resolveUpload(http, create.image, options, 'colour');
91
+ const depth = create.depth
92
+ ? {
93
+ image: await resolveUpload(http, create.depth.image, options, 'depth'),
94
+ ...(create.depth.depthMapType !== undefined
95
+ ? { depthMapType: create.depth.depthMapType }
96
+ : {}),
97
+ ...(create.depth.metersPerDepth !== undefined
98
+ ? { metersPerDepth: create.depth.metersPerDepth }
99
+ : {})
100
+ }
101
+ : undefined;
102
+ const metersPerDepth = depth?.metersPerDepth;
103
+ if (metersPerDepth !== undefined && (!Number.isFinite(metersPerDepth) || metersPerDepth <= 0)) {
104
+ throw new LiformaPublisherError('depth.metersPerDepth must be a positive number.', {
105
+ code: 'INVALID_BODY'
106
+ });
107
+ }
108
+ const started = await startJobWithOutcome(http, '/backdrops', createBackdropPublishResponseSchema, {
109
+ name: create.name?.trim() || defaultName,
110
+ uploadId: typeof image === 'string' ? image : image.uploadId,
111
+ ...(create.externalId !== undefined ? { externalId: create.externalId } : {}),
112
+ ...(create.forceNew !== undefined ? { forceNew: create.forceNew } : {}),
113
+ ...(depth
114
+ ? {
115
+ depth: {
116
+ uploadId: typeof depth.image === 'string' ? depth.image : depth.image.uploadId,
117
+ ...(depth.depthMapType !== undefined
118
+ ? { depthMapType: depth.depthMapType }
119
+ : {}),
120
+ ...(metersPerDepth !== undefined ? { metersPerDepth } : {})
121
+ }
122
+ }
123
+ : {})
124
+ }, options.signal);
125
+ const completed = await waitPlate(http, started.job.id, 'backdrop', options);
126
+ return {
127
+ backdrop: await getBackdrop(http, completed.targetId, options),
128
+ created: started.created
129
+ };
130
+ });
131
+ }
132
+ async function resolveWardrobePlate(http, args) {
133
+ const { plate, stage, options } = args;
134
+ const id = reuseId(plate);
135
+ if (id) {
136
+ return runStep(stage, async () => {
137
+ const { data } = await publisherRequest(http, projectPath(http, args.getPath(id)), args.getSchema, { method: 'GET', signal: options.signal, retry: true });
138
+ return { resource: args.pick(data), created: false };
139
+ });
140
+ }
141
+ if (!('image' in plate)) {
142
+ throw new LiformaPublisherError(`${stage} must be { id } or include image.`, {
143
+ code: 'INVALID_BODY'
144
+ });
145
+ }
146
+ const create = plate;
147
+ return runStep(stage, async () => {
148
+ const image = await resolveUpload(http, create.image, options, 'colour');
149
+ const started = await startJobWithOutcome(http, args.path, args.startSchema, wardrobePublishBody({
150
+ avatarId: args.avatarId,
151
+ image,
152
+ name: create.name,
153
+ backgroundMode: create.backgroundMode ?? 'remove',
154
+ externalId: create.externalId,
155
+ forceNew: create.forceNew
156
+ }), options.signal);
157
+ const completed = await waitPlate(http, started.job.id, stage, options);
158
+ const { data } = await publisherRequest(http, projectPath(http, args.getPath(completed.targetId)), args.getSchema, { method: 'GET', signal: options.signal, retry: true });
159
+ return { resource: args.pick(data), created: started.created };
160
+ });
161
+ }
162
+ function assertAppearanceExclusive(character) {
163
+ const hasCostume = character.costume !== undefined;
164
+ const hasClothes = character.clothes !== undefined;
165
+ const hasHair = character.hair !== undefined;
166
+ if (hasCostume && (hasClothes || hasHair)) {
167
+ throw new LiformaPublisherError('Provide costume or clothes+hair, not both.', {
168
+ code: 'INVALID_BODY'
169
+ });
170
+ }
171
+ if (hasClothes !== hasHair) {
172
+ throw new LiformaPublisherError('clothes and hair must both be provided, or both omitted.', { code: 'INVALID_BODY' });
173
+ }
174
+ }
175
+ async function resolveAppearance(http, character, options) {
176
+ assertAppearanceExclusive(character);
177
+ if (character.costume) {
178
+ const costume = await resolveWardrobePlate(http, {
179
+ plate: character.costume,
180
+ stage: 'costume',
181
+ avatarId: character.avatarId,
182
+ path: '/costumes',
183
+ startSchema: createCostumePublishResponseSchema,
184
+ getPath: (id) => `/costumes/${encodeURIComponent(id)}`,
185
+ getSchema: getCostumeResponseSchema,
186
+ pick: (data) => data.costume,
187
+ options
188
+ });
189
+ return {
190
+ costume: costume.resource,
191
+ costumeId: costume.resource.id,
192
+ created: { costume: costume.created }
193
+ };
194
+ }
195
+ if (character.clothes && character.hair) {
196
+ const [clothes, hair] = await Promise.all([
197
+ resolveWardrobePlate(http, {
198
+ plate: character.clothes,
199
+ stage: 'clothes',
200
+ avatarId: character.avatarId,
201
+ path: '/clothes',
202
+ startSchema: createClothesPublishResponseSchema,
203
+ getPath: (id) => `/clothes/${encodeURIComponent(id)}`,
204
+ getSchema: getClothesResponseSchema,
205
+ pick: (data) => data.clothes,
206
+ options
207
+ }),
208
+ resolveWardrobePlate(http, {
209
+ plate: character.hair,
210
+ stage: 'hair',
211
+ avatarId: character.avatarId,
212
+ path: '/hair',
213
+ startSchema: createHairPublishResponseSchema,
214
+ getPath: (id) => `/hair/${encodeURIComponent(id)}`,
215
+ getSchema: getHairResponseSchema,
216
+ pick: (data) => data.hair,
217
+ options
218
+ })
219
+ ]);
220
+ return {
221
+ clothes: clothes.resource,
222
+ hair: hair.resource,
223
+ clothesId: clothes.resource.id,
224
+ hairId: hair.resource.id,
225
+ created: { clothes: clothes.created, hair: hair.created }
226
+ };
227
+ }
228
+ return { created: {} };
229
+ }
230
+ function characterCreateBody(body, appearance) {
231
+ return {
232
+ avatarId: requiredString(body.avatarId, 'character.avatarId'),
233
+ name: requiredString(body.name, 'character.name'),
234
+ voice: requiredString(body.voice, 'character.voice'),
235
+ sttLang: requiredString(body.sttLang, 'character.sttLang'),
236
+ ...(appearance?.costumeId !== undefined ? { costumeId: appearance.costumeId } : {}),
237
+ ...(appearance?.clothesId !== undefined ? { clothesId: appearance.clothesId } : {}),
238
+ ...(appearance?.hairId !== undefined ? { hairId: appearance.hairId } : {}),
239
+ ...(body.personality !== undefined ? { personality: body.personality } : {}),
240
+ ...(body.generalInstructions !== undefined
241
+ ? { generalInstructions: body.generalInstructions }
242
+ : {}),
243
+ ...(body.gender !== undefined ? { gender: body.gender } : {}),
244
+ ...(body.age !== undefined ? { age: body.age } : {}),
245
+ ...(body.ethnicity !== undefined ? { ethnicity: body.ethnicity } : {}),
246
+ ...(body.externalId !== undefined ? { externalId: body.externalId } : {})
247
+ };
248
+ }
249
+ function parseCharacterInput(input) {
250
+ const id = reuseId(input);
251
+ if (id) {
252
+ return { reuseId: id, create: null };
253
+ }
254
+ if (!('avatarId' in input)) {
255
+ throw new LiformaPublisherError('character must be { id } or include avatarId, name, voice, and sttLang.', { code: 'INVALID_BODY' });
256
+ }
257
+ requiredString(input.avatarId, 'character.avatarId');
258
+ requiredString(input.name, 'character.name');
259
+ requiredString(input.voice, 'character.voice');
260
+ requiredString(input.sttLang, 'character.sttLang');
261
+ assertAppearanceExclusive(input);
262
+ return { reuseId: null, create: input };
263
+ }
264
+ async function loadAppearanceFromCharacter(http, character, options) {
265
+ const created = {};
266
+ const out = { created };
267
+ if (character.costumeId) {
268
+ const { data } = await publisherRequest(http, projectPath(http, `/costumes/${encodeURIComponent(character.costumeId)}`), getCostumeResponseSchema, { method: 'GET', signal: options.signal, retry: true });
269
+ out.costume = data.costume;
270
+ out.costumeId = data.costume.id;
271
+ created.costume = false;
272
+ }
273
+ if (character.clothesId) {
274
+ const { data } = await publisherRequest(http, projectPath(http, `/clothes/${encodeURIComponent(character.clothesId)}`), getClothesResponseSchema, { method: 'GET', signal: options.signal, retry: true });
275
+ out.clothes = data.clothes;
276
+ out.clothesId = data.clothes.id;
277
+ created.clothes = false;
278
+ }
279
+ if (character.hairId) {
280
+ const { data } = await publisherRequest(http, projectPath(http, `/hair/${encodeURIComponent(character.hairId)}`), getHairResponseSchema, { method: 'GET', signal: options.signal, retry: true });
281
+ out.hair = data.hair;
282
+ out.hairId = data.hair.id;
283
+ created.hair = false;
284
+ }
285
+ return out;
286
+ }
287
+ /**
288
+ * Resolve character with externalId-safe wardrobe:
289
+ * - reuse `{ id }` → GET, no wardrobe create
290
+ * - create with externalId → probe create without appearance; on reuse skip wardrobe;
291
+ * on new create, build wardrobe then PATCH appearance
292
+ * - create without externalId → wardrobe first, then create with appearance
293
+ */
294
+ async function resolveCharacter(http, parsed, options, appearanceFactory) {
295
+ return runStep('character', async () => {
296
+ if (parsed.reuseId) {
297
+ const character = await getCharacter(http, parsed.reuseId, options);
298
+ const appearance = await loadAppearanceFromCharacter(http, character, options);
299
+ return { character, created: false, appearance };
300
+ }
301
+ const body = parsed.create;
302
+ const wantsAppearance = body.costume !== undefined || body.clothes !== undefined || body.hair !== undefined;
303
+ if (body.externalId) {
304
+ const { data, status } = await publisherRequest(http, projectPath(http, '/characters'), createCharacterResponseSchema, {
305
+ method: 'POST',
306
+ body: characterCreateBody(body),
307
+ signal: options.signal
308
+ });
309
+ const created = status === 201;
310
+ if (!created) {
311
+ const appearance = await loadAppearanceFromCharacter(http, data.character, options);
312
+ return { character: data.character, created: false, appearance };
313
+ }
314
+ if (!wantsAppearance) {
315
+ return { character: data.character, created: true, appearance: { created: {} } };
316
+ }
317
+ const appearance = await appearanceFactory();
318
+ const { data: patched } = await publisherRequest(http, projectPath(http, `/characters/${encodeURIComponent(data.character.id)}`), createCharacterResponseSchema, {
319
+ method: 'PATCH',
320
+ body: {
321
+ ...(appearance.costumeId !== undefined
322
+ ? { costumeId: appearance.costumeId }
323
+ : {}),
324
+ ...(appearance.clothesId !== undefined
325
+ ? { clothesId: appearance.clothesId }
326
+ : {}),
327
+ ...(appearance.hairId !== undefined ? { hairId: appearance.hairId } : {})
328
+ },
329
+ signal: options.signal,
330
+ retry: true
331
+ });
332
+ return { character: patched.character, created: true, appearance };
333
+ }
334
+ const appearance = wantsAppearance
335
+ ? await appearanceFactory()
336
+ : { created: {} };
337
+ const { data, status } = await publisherRequest(http, projectPath(http, '/characters'), createCharacterResponseSchema, {
338
+ method: 'POST',
339
+ body: characterCreateBody(body, appearance),
340
+ signal: options.signal
341
+ });
342
+ return {
343
+ character: data.character,
344
+ created: status === 201,
345
+ appearance
346
+ };
347
+ });
348
+ }
349
+ export async function createExperienceFrom(http, input, options = {}) {
350
+ requireInput(input);
351
+ const title = requiredString(input.title, 'title');
352
+ if (!input.backdrop || typeof input.backdrop !== 'object') {
353
+ throw new LiformaPublisherError('backdrop is required.', { code: 'INVALID_BODY' });
354
+ }
355
+ if (!input.character || typeof input.character !== 'object') {
356
+ throw new LiformaPublisherError('character is required.', { code: 'INVALID_BODY' });
357
+ }
358
+ const defaultName = title;
359
+ let parsedCharacter;
360
+ try {
361
+ parsedCharacter = parseCharacterInput(input.character);
362
+ }
363
+ catch (error) {
364
+ throw withContext(error, 'character');
365
+ }
366
+ const appearanceFactory = () => {
367
+ if (!parsedCharacter.create) {
368
+ return Promise.resolve({ created: {} });
369
+ }
370
+ return resolveAppearance(http, parsedCharacter.create, options);
371
+ };
372
+ // Backdrop can run in parallel with character probe / wardrobe when safe.
373
+ // Character with externalId probes first (inside resolveCharacter) before wardrobe.
374
+ const backdropPromise = resolveBackdrop(http, input.backdrop, options, defaultName);
375
+ const characterNeedsWardrobeFirst = parsedCharacter.create &&
376
+ !parsedCharacter.create.externalId &&
377
+ (parsedCharacter.create.costume !== undefined ||
378
+ parsedCharacter.create.clothes !== undefined ||
379
+ parsedCharacter.create.hair !== undefined);
380
+ let backdropOutcome;
381
+ let characterOutcome;
382
+ if (characterNeedsWardrobeFirst) {
383
+ const [backdrop, appearance] = await Promise.all([
384
+ backdropPromise,
385
+ appearanceFactory()
386
+ ]);
387
+ backdropOutcome = backdrop;
388
+ characterOutcome = await resolveCharacter(http, parsedCharacter, options, async () => appearance);
389
+ }
390
+ else {
391
+ const [backdrop, character] = await Promise.all([
392
+ backdropPromise,
393
+ resolveCharacter(http, parsedCharacter, options, appearanceFactory)
394
+ ]);
395
+ backdropOutcome = backdrop;
396
+ characterOutcome = character;
397
+ }
398
+ const setOutcome = await runStep('set', async () => {
399
+ const { data, status } = await publisherRequest(http, projectPath(http, '/sets'), createSetResponseSchema, {
400
+ method: 'POST',
401
+ body: {
402
+ backdropId: backdropOutcome.backdrop.id,
403
+ ...(input.set?.name !== undefined
404
+ ? { name: input.set.name }
405
+ : { name: defaultName }),
406
+ ...(input.set?.externalId !== undefined
407
+ ? { externalId: input.set.externalId }
408
+ : {})
409
+ },
410
+ signal: options.signal
411
+ });
412
+ const created = status === 201;
413
+ if (!created) {
414
+ // Idempotent set reuse: return the set's real backdrop for a coherent snapshot.
415
+ const set = data.set;
416
+ const backdrop = await getBackdrop(http, set.backdropId, options);
417
+ backdropOutcome = { backdrop, created: false };
418
+ return { set, created: false };
419
+ }
420
+ return { set: data.set, created: true };
421
+ });
422
+ const experienceOutcome = await runStep('experience', async () => {
423
+ const { data, status } = await publisherRequest(http, projectPath(http, '/experiences'), experienceResponseSchema, {
424
+ method: 'POST',
425
+ body: {
426
+ title,
427
+ characterId: characterOutcome.character.id,
428
+ setId: setOutcome.set.id,
429
+ ...(input.slug !== undefined ? { slug: input.slug } : {}),
430
+ ...(input.attributes !== undefined ? { attributes: input.attributes } : {}),
431
+ ...(input.startingMessage !== undefined
432
+ ? { startingMessage: input.startingMessage }
433
+ : {}),
434
+ ...(input.systemInstructions !== undefined
435
+ ? { systemInstructions: input.systemInstructions }
436
+ : {}),
437
+ ...(input.introduction !== undefined ? { introduction: input.introduction } : {}),
438
+ ...(input.interaction !== undefined ? { interaction: input.interaction } : {}),
439
+ ...(input.publish !== undefined ? { publish: input.publish } : {}),
440
+ ...(input.externalId !== undefined ? { externalId: input.externalId } : {})
441
+ },
442
+ signal: options.signal
443
+ });
444
+ return { experience: data.experience, created: status === 201 };
445
+ });
446
+ // Experience externalId reuse: rebuild composition from the returned experience
447
+ // so the snapshot matches what is actually live (not orphans from this call).
448
+ if (!experienceOutcome.created) {
449
+ const experience = experienceOutcome.experience;
450
+ const characterId = experience.characterId;
451
+ const setId = experience.setId ?? experience.placeId;
452
+ if (!characterId || !setId) {
453
+ throw new LiformaPublisherError('Reused experience is missing characterId or setId.', { code: 'INVALID_RESPONSE' });
454
+ }
455
+ const character = await getCharacter(http, characterId, options);
456
+ const set = await getSet(http, setId, options);
457
+ const backdrop = await getBackdrop(http, set.backdropId, options);
458
+ const appearance = await loadAppearanceFromCharacter(http, character, options);
459
+ return {
460
+ experience,
461
+ character,
462
+ set,
463
+ backdrop,
464
+ ...(appearance.costume ? { costume: appearance.costume } : {}),
465
+ ...(appearance.clothes ? { clothes: appearance.clothes } : {}),
466
+ ...(appearance.hair ? { hair: appearance.hair } : {}),
467
+ created: {
468
+ experience: false,
469
+ character: false,
470
+ set: false,
471
+ backdrop: false,
472
+ ...appearance.created
473
+ }
474
+ };
475
+ }
476
+ const appearance = characterOutcome.appearance;
477
+ return {
478
+ experience: experienceOutcome.experience,
479
+ character: characterOutcome.character,
480
+ set: setOutcome.set,
481
+ backdrop: backdropOutcome.backdrop,
482
+ ...(appearance.costume ? { costume: appearance.costume } : {}),
483
+ ...(appearance.clothes ? { clothes: appearance.clothes } : {}),
484
+ ...(appearance.hair ? { hair: appearance.hair } : {}),
485
+ created: {
486
+ experience: experienceOutcome.created,
487
+ character: characterOutcome.created,
488
+ set: setOutcome.created,
489
+ backdrop: backdropOutcome.created,
490
+ ...appearance.created
491
+ }
492
+ };
493
+ }
494
+ //# sourceMappingURL=createFrom.js.map