@theaiplatform/miniapp-sdk 0.0.0

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.
@@ -0,0 +1,35 @@
1
+ export declare type AppConfig = {
2
+ ui?: readonly UIEntrypoint[];
3
+ tools?: Record<string, ToolDefinition>;
4
+ };
5
+
6
+ export declare function defineApp<const TConfig extends AppConfig>(config: TConfig): TConfig;
7
+
8
+ export declare type JsonSchema = {
9
+ readonly [key: string]: unknown;
10
+ };
11
+
12
+ export declare type ToolDefinition<TArguments = unknown, TResult = unknown> = {
13
+ displayName?: string;
14
+ description: string;
15
+ parametersSchema: JsonSchema;
16
+ timeoutMs?: number;
17
+ actionMessageTemplate?: string;
18
+ execute(arguments_: TArguments): TResult;
19
+ };
20
+
21
+ export declare type UIEntrypoint = {
22
+ type: UIEntrypointType;
23
+ id: string;
24
+ name: string;
25
+ entryPoint: string;
26
+ icon?: string;
27
+ persistWhenClosed?: boolean;
28
+ };
29
+
30
+ /**
31
+ * @capability integrations-and-marketplace
32
+ */
33
+ export declare type UIEntrypointType = 'workspace-left-sidebar' | 'workspace-left-sidebar-item' | 'chat-right-sidebar' | 'chat-right-sidebar-item';
34
+
35
+ export { }
package/dist/config.js ADDED
@@ -0,0 +1,4 @@
1
+ function defineApp(config) {
2
+ return config;
3
+ }
4
+ export { defineApp };
@@ -0,0 +1,475 @@
1
+ export declare type AppConfig = {
2
+ ui?: readonly UIEntrypoint[];
3
+ tools?: Record<string, ToolDefinition>;
4
+ };
5
+
6
+ export declare type CreateChannelOptions = {
7
+ workspaceId?: string;
8
+ name: string;
9
+ description?: string;
10
+ projectId?: string | null;
11
+ visibility?: string;
12
+ };
13
+
14
+ export declare type CreateChannelResult = {
15
+ roomId: string;
16
+ };
17
+
18
+ export declare type CreateProjectOptions = {
19
+ workspaceId?: string;
20
+ id?: string;
21
+ name: string;
22
+ discoverable?: boolean;
23
+ };
24
+
25
+ export declare type CreateProjectResult = {
26
+ projectId: string;
27
+ };
28
+
29
+ export declare function defineApp<const TConfig extends AppConfig>(config: TConfig): TConfig;
30
+
31
+ export declare type GetChannelAccessOptions = {
32
+ workspaceId?: string;
33
+ channelId: string;
34
+ };
35
+
36
+ export declare type GetChannelAccessResult = {
37
+ archived: boolean;
38
+ capabilities: string[];
39
+ isParticipant: boolean;
40
+ /**
41
+ * Last timeline sequence retained for a former participant. When present,
42
+ * the channel remains readable through this sequence even though
43
+ * `isParticipant` is false.
44
+ */
45
+ visibleUntilSequence?: number | null;
46
+ };
47
+
48
+ export declare type GetChannelTimelineOptions = {
49
+ workspaceId?: string;
50
+ channelId: string;
51
+ };
52
+
53
+ export declare type GetChannelTimelineResult = {
54
+ timeline: {
55
+ messages: MiniAppChannelMessage[];
56
+ sequence: number;
57
+ };
58
+ };
59
+
60
+ export declare type GetProjectOptions = {
61
+ workspaceId?: string;
62
+ projectId: string;
63
+ };
64
+
65
+ export declare type GetProjectResult = {
66
+ project: MiniAppProject | null;
67
+ };
68
+
69
+ export declare type InvokeSavedWorkflowOptions<TPayload = unknown> = {
70
+ workflowId: string;
71
+ payload?: TPayload;
72
+ };
73
+
74
+ export declare type InvokeWorkflowOptions<TPayload = unknown> = {
75
+ workflowJson: string;
76
+ payload?: TPayload;
77
+ };
78
+
79
+ export declare type InvokeWorkflowResult = {
80
+ success: boolean;
81
+ status: string;
82
+ message: string;
83
+ runId?: string | null;
84
+ error?: string | null;
85
+ };
86
+
87
+ export declare type JsonSchema = {
88
+ readonly [key: string]: unknown;
89
+ };
90
+
91
+ export declare type ListChannelsOptions = {
92
+ workspaceId?: string;
93
+ };
94
+
95
+ export declare type ListChannelsResult = {
96
+ rooms: MiniAppChannel[];
97
+ readMode: string;
98
+ };
99
+
100
+ export declare type ListWorkflowsOptions = {
101
+ workspaceId?: string;
102
+ };
103
+
104
+ export declare type ListWorkflowsResult = {
105
+ workflows: MiniAppWorkflow[];
106
+ };
107
+
108
+ export declare type MiniAppAuthApi = {
109
+ getUserProfile(): MiniAppMaybePromise<MiniAppUserProfile | null>;
110
+ };
111
+
112
+ /**
113
+ * @capability integrations-and-marketplace
114
+ */
115
+ export declare type MiniAppChannel = {
116
+ roomId: string;
117
+ title?: string;
118
+ kind?: string;
119
+ description?: string;
120
+ visibility: string;
121
+ archived: boolean;
122
+ createdAt: number;
123
+ updatedAt: number;
124
+ };
125
+
126
+ /**
127
+ * One bounded JSON timeline row. The row schema is host-versioned, so callers
128
+ * must narrow it before reading fields.
129
+ */
130
+ export declare type MiniAppChannelMessage = MiniAppJsonValue;
131
+
132
+ export declare type MiniAppChatApi = {
133
+ /**
134
+ * Select the miniapp's active conversation, reveal the shared chat panel,
135
+ * and place text in its composer without unmounting the miniapp surface.
136
+ */
137
+ sendTextToChat(text: string): MiniAppMaybePromise<void>;
138
+ joinChannelSpecialist?(channelId: string, specialistId: string): MiniAppMaybePromise<null>;
139
+ archiveConversation?(workspaceId: string, userId: string, conversationId: string, projectId: string): MiniAppMaybePromise<null>;
140
+ };
141
+
142
+ export declare type MiniAppCreateSpecialistOptions = {
143
+ name: string;
144
+ domain: string;
145
+ description: string | null;
146
+ configuration: MiniAppJsonValue;
147
+ };
148
+
149
+ export declare type MiniAppCreateSpecialistResult = {
150
+ id: string;
151
+ name: string;
152
+ domain: string;
153
+ description: string | null;
154
+ isActive: boolean;
155
+ };
156
+
157
+ /** JSON-compatible values accepted by public miniapp operations. */
158
+ export declare type MiniAppJsonValue = null | boolean | number | string | MiniAppJsonValue[] | {
159
+ [key: string]: MiniAppJsonValue;
160
+ };
161
+
162
+ /**
163
+ * Author-controlled manifest accepted by the host-managed specialist
164
+ * persistence operation. Ownership, visibility, installation, and verification
165
+ * metadata are derived by the host and cannot be supplied by a miniapp.
166
+ */
167
+ export declare type MiniAppManagedSpecialist = {
168
+ id: string;
169
+ slug: string;
170
+ name: string;
171
+ publisher: string;
172
+ description: string;
173
+ icon: string;
174
+ category?: string;
175
+ categoryDisplayName?: string;
176
+ version?: string;
177
+ schemaVersion?: string;
178
+ displayName?: string;
179
+ maintainers?: Array<{
180
+ name: string;
181
+ email: string;
182
+ url?: string;
183
+ }>;
184
+ license?: string;
185
+ lastUpdated?: string;
186
+ fullDescription?: string;
187
+ knowledgeSources?: MiniAppJsonValue[];
188
+ ownedKnowledgePlotId?: string;
189
+ links?: {
190
+ terms?: string;
191
+ privacy?: string;
192
+ website?: string;
193
+ };
194
+ systemPrompt?: string;
195
+ prompts?: MiniAppJsonValue;
196
+ skills?: string[];
197
+ tooling?: MiniAppJsonValue;
198
+ orchestration?: MiniAppJsonValue;
199
+ taskModels?: Record<string, string>;
200
+ knowledgeTargeting?: MiniAppJsonValue;
201
+ tasks?: MiniAppJsonValue[];
202
+ identity?: MiniAppJsonValue;
203
+ audience?: MiniAppJsonValue;
204
+ constraints?: MiniAppJsonValue;
205
+ domainContext?: MiniAppJsonValue;
206
+ purpose?: string;
207
+ values?: MiniAppJsonValue[];
208
+ attributes?: string[];
209
+ techStack?: string[];
210
+ writingStyle?: MiniAppJsonValue;
211
+ tags?: string[];
212
+ preferredModel?: string;
213
+ supportsLocal?: boolean;
214
+ requiresNetwork?: boolean;
215
+ knowledgeGardens?: string[];
216
+ };
217
+
218
+ export declare type MiniAppManagedSpecialistResult = {
219
+ specialistId: string;
220
+ };
221
+
222
+ export declare type MiniAppMaybePromise<T> = T | Promise<T>;
223
+
224
+ export declare type MiniAppPlatformApi = {
225
+ channels: {
226
+ create(options: CreateChannelOptions): CreateChannelResult | Promise<CreateChannelResult>;
227
+ list(options?: ListChannelsOptions): ListChannelsResult | Promise<ListChannelsResult>;
228
+ sendMessage(options: SendChannelMessageOptions): SendChannelMessageResult | Promise<SendChannelMessageResult>;
229
+ /** Persist one deterministic completed row under a joined specialist. */
230
+ sendSpecialistMessage(options: SendChannelSpecialistMessageOptions): SendChannelSpecialistMessageResult | Promise<SendChannelSpecialistMessageResult>;
231
+ getAccess(options: GetChannelAccessOptions): GetChannelAccessResult | Promise<GetChannelAccessResult>;
232
+ getTimeline(options: GetChannelTimelineOptions): GetChannelTimelineResult | Promise<GetChannelTimelineResult>;
233
+ };
234
+ projects: {
235
+ create(options: CreateProjectOptions): CreateProjectResult | Promise<CreateProjectResult>;
236
+ get(options: GetProjectOptions): GetProjectResult | Promise<GetProjectResult>;
237
+ update(options: UpdateProjectOptions): UpdateProjectResult | Promise<UpdateProjectResult>;
238
+ };
239
+ workflows: {
240
+ list(options?: ListWorkflowsOptions): ListWorkflowsResult | Promise<ListWorkflowsResult>;
241
+ invokeSaved<TPayload = unknown>(options: InvokeSavedWorkflowOptions<TPayload>): InvokeWorkflowResult | Promise<InvokeWorkflowResult>;
242
+ invoke?<TPayload = unknown>(options: InvokeWorkflowOptions<TPayload>): InvokeWorkflowResult | Promise<InvokeWorkflowResult>;
243
+ };
244
+ navigation: {
245
+ open(options: OpenNavigationOptions): void | Promise<void>;
246
+ };
247
+ chat: MiniAppChatApi;
248
+ /** Browser capabilities appear only when the selected target supports them. */
249
+ auth?: MiniAppAuthApi;
250
+ vfs?: MiniAppVfsApi;
251
+ specialist?: MiniAppSpecialistApi;
252
+ hasEditorView?: boolean;
253
+ hasHostHttpRequest?: boolean;
254
+ };
255
+
256
+ export declare type MiniAppProject = {
257
+ id: string;
258
+ name: string;
259
+ workspaceId: string;
260
+ discoverable: boolean;
261
+ channelIds: string[];
262
+ archivedChannelIds: string[];
263
+ };
264
+
265
+ export declare type MiniAppProvisionProjectChatOptions = {
266
+ conversationId: string;
267
+ projectId: string;
268
+ referenceBranch?: string | null;
269
+ };
270
+
271
+ export declare type MiniAppProvisionProjectChatResult = {
272
+ mountedRoots: string[];
273
+ worktreeBranch?: string | null;
274
+ worktreeBaseCommit?: string | null;
275
+ };
276
+
277
+ export declare type MiniAppSpecialistApi = {
278
+ joinToChannel(channelId: string, specialistId: string): MiniAppMaybePromise<string>;
279
+ listWorkspace(workspaceId: string): MiniAppMaybePromise<MiniAppSpecialistSummary[]>;
280
+ create(options: MiniAppCreateSpecialistOptions): MiniAppMaybePromise<MiniAppCreateSpecialistResult>;
281
+ upsertManaged?(specialist: MiniAppManagedSpecialist): MiniAppMaybePromise<MiniAppManagedSpecialistResult>;
282
+ runTurnWithTools?(options: MiniAppSpecialistTurnOptions): MiniAppMaybePromise<MiniAppSpecialistTurnResult>;
283
+ };
284
+
285
+ export declare type MiniAppSpecialistConversationPart = {
286
+ type: 'text';
287
+ content: string;
288
+ } | {
289
+ type: 'tool';
290
+ toolCallId: string;
291
+ toolName: string;
292
+ arguments: MiniAppJsonValue;
293
+ toolIntent: string | null;
294
+ success: boolean;
295
+ content: MiniAppJsonValue;
296
+ mediaCost?: MiniAppJsonValue | null;
297
+ error?: string | null;
298
+ executionTimeMs: number;
299
+ } | {
300
+ type: 'stepEnd';
301
+ iteration: number;
302
+ };
303
+
304
+ export declare type MiniAppSpecialistInteractionMode = 'conversational' | 'agentic' | 'planning' | 'background' | 'longRunning' | 'inlineEdit' | 'focus';
305
+
306
+ export declare type MiniAppSpecialistSummary = {
307
+ id: string;
308
+ slug: string;
309
+ displayName: string;
310
+ description: string;
311
+ tags: string[];
312
+ version: string;
313
+ availability: 'public' | 'internal' | 'private' | 'restricted';
314
+ canRunLocally: boolean;
315
+ category: string;
316
+ categoryDisplayName: string;
317
+ requiredCapabilities: {
318
+ inputModalities: string[];
319
+ toolUse?: boolean | null;
320
+ reasoning?: boolean | null;
321
+ } | null;
322
+ omnipresence?: {
323
+ autoJoinChannels: boolean;
324
+ includeDms: boolean;
325
+ pinSidebar: boolean;
326
+ } | null;
327
+ displayIcon?: {
328
+ type: 'bundled';
329
+ path: string;
330
+ } | {
331
+ type: 'remote';
332
+ url: string;
333
+ } | null;
334
+ };
335
+
336
+ export declare type MiniAppSpecialistTurnOptions = {
337
+ workspaceId: string;
338
+ channelId: string;
339
+ specialistId: string;
340
+ content: string;
341
+ modelOverride: string;
342
+ messageId: null;
343
+ interactionMode: MiniAppSpecialistInteractionMode;
344
+ timeoutMs: number;
345
+ };
346
+
347
+ export declare type MiniAppSpecialistTurnResult = {
348
+ completionEvent: {
349
+ parts: MiniAppSpecialistConversationPart[];
350
+ finishReason?: string;
351
+ modelUsed?: string;
352
+ };
353
+ };
354
+
355
+ export declare type MiniAppUserProfile = {
356
+ sub: string;
357
+ name?: string | null;
358
+ givenName?: string | null;
359
+ familyName?: string | null;
360
+ middleName?: string | null;
361
+ nickname?: string | null;
362
+ preferredUsername?: string | null;
363
+ profile?: string | null;
364
+ picture?: string | null;
365
+ website?: string | null;
366
+ email?: string | null;
367
+ emailVerified?: boolean | null;
368
+ gender?: string | null;
369
+ birthdate?: string | null;
370
+ zoneinfo?: string | null;
371
+ locale?: string | null;
372
+ phoneNumber?: string | null;
373
+ phoneNumberVerified?: boolean | null;
374
+ address?: MiniAppJsonValue | null;
375
+ updatedAt?: string | null;
376
+ };
377
+
378
+ export declare type MiniAppVfsApi = {
379
+ provisionProjectChat(options: MiniAppProvisionProjectChatOptions): MiniAppMaybePromise<MiniAppProvisionProjectChatResult>;
380
+ writeFile(conversationId: string, path: string, data: Uint8Array): MiniAppMaybePromise<null>;
381
+ mkdir(conversationId: string, path: string): MiniAppMaybePromise<null>;
382
+ };
383
+
384
+ export declare type MiniAppWorkflow = {
385
+ id: string;
386
+ name: string;
387
+ type: string;
388
+ createdAt: number;
389
+ updatedAt: number;
390
+ };
391
+
392
+ export declare type OpenNavigationOptions = {
393
+ path: string;
394
+ };
395
+
396
+ /**
397
+ * Public miniapp API installed by the host. Importing this value is safe in
398
+ * build tools and tests; an unsupported-environment error is raised only when a
399
+ * property is read before the host installs the API.
400
+ */
401
+ export declare const sdk: MiniAppPlatformApi;
402
+
403
+ export declare type SendChannelMessageOptions = {
404
+ workspaceId?: string;
405
+ channelId: string;
406
+ /**
407
+ * Optional stable client id for product-owned messages such as seeded
408
+ * lifecycle notices. Omit for ordinary miniapp messages.
409
+ */
410
+ clientMessageId?: string;
411
+ name?: string;
412
+ /** Display body to persist. When omitted, the host formats `name` and `content`. */
413
+ body?: string;
414
+ content: string;
415
+ /** Optional structured chat content persisted with the message. */
416
+ messageContent?: Record<string, MiniAppJsonValue>;
417
+ };
418
+
419
+ export declare type SendChannelMessageResult = {
420
+ messageId: string;
421
+ clientMessageId: string;
422
+ };
423
+
424
+ export declare type SendChannelSpecialistMessageOptions = {
425
+ workspaceId?: string;
426
+ channelId: string;
427
+ projectId: string;
428
+ specialistId: string;
429
+ clientMessageId: string;
430
+ body: string;
431
+ messageContent?: Record<string, MiniAppJsonValue>;
432
+ };
433
+
434
+ export declare type SendChannelSpecialistMessageResult = {
435
+ messageId: string;
436
+ clientMessageId: string;
437
+ };
438
+
439
+ export declare type ToolDefinition<TArguments = unknown, TResult = unknown> = {
440
+ displayName?: string;
441
+ description: string;
442
+ parametersSchema: JsonSchema;
443
+ timeoutMs?: number;
444
+ actionMessageTemplate?: string;
445
+ execute(arguments_: TArguments): TResult;
446
+ };
447
+
448
+ export declare type UIEntrypoint = {
449
+ type: UIEntrypointType;
450
+ id: string;
451
+ name: string;
452
+ entryPoint: string;
453
+ icon?: string;
454
+ persistWhenClosed?: boolean;
455
+ };
456
+
457
+ /**
458
+ * @capability integrations-and-marketplace
459
+ */
460
+ export declare type UIEntrypointType = 'workspace-left-sidebar' | 'workspace-left-sidebar-item' | 'chat-right-sidebar' | 'chat-right-sidebar-item';
461
+
462
+ export declare type UpdateProjectOptions = {
463
+ workspaceId?: string;
464
+ projectId: string;
465
+ name?: string;
466
+ discoverable?: boolean;
467
+ channelIds?: string[];
468
+ archivedChannelIds?: string[];
469
+ };
470
+
471
+ export declare type UpdateProjectResult = {
472
+ project: MiniAppProject;
473
+ };
474
+
475
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { defineApp } from "./config.js";
2
+ export { sdk } from "./sdk.js";
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @capability integrations-and-marketplace
3
+ */
4
+
5
+ const getLoaderOptions = (loaderContext) => {
6
+ if (typeof loaderContext.getOptions === 'function') {
7
+ const options = loaderContext.getOptions();
8
+
9
+ if (options && Object.keys(options).length > 0) {
10
+ return options;
11
+ }
12
+ }
13
+
14
+ if (typeof loaderContext.query !== 'string') {
15
+ return {};
16
+ }
17
+
18
+ return Object.fromEntries(
19
+ new URLSearchParams(loaderContext.query.replace(/^\?/, '')),
20
+ );
21
+ };
22
+
23
+ module.exports = function tapCssEntryLoader() {
24
+ const options = getLoaderOptions(this);
25
+
26
+ return `import ${JSON.stringify(options.source)};`;
27
+ };
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @capability integrations-and-marketplace
3
+ */
4
+
5
+ const crypto = require('node:crypto');
6
+ const fs = require('node:fs');
7
+ const path = require('node:path');
8
+
9
+ const SCRIPT_SRC_PATTERN = /<script\b([^>]*?)\bsrc=(["'])([^"']+)\2([^>]*)>/gi;
10
+ const STYLESHEET_HREF_PATTERN =
11
+ /<link\b(?=[^>]*?\brel=(["'])stylesheet\1)([^>]*?)\bhref=(["'])([^"']+)\3([^>]*)>/gi;
12
+ const SCRIPT_SRC_TOKEN_PREFIX = '__TAP_HTML_SCRIPT:';
13
+ const STYLESHEET_HREF_TOKEN_PREFIX = '__TAP_HTML_STYLESHEET:';
14
+
15
+ const isUrl = (value) => {
16
+ try {
17
+ new URL(value);
18
+ return true;
19
+ } catch {
20
+ return false;
21
+ }
22
+ };
23
+
24
+ const stripQuery = (value) => value.split('?')[0];
25
+
26
+ const isLocalReference = (value) =>
27
+ !isUrl(value) &&
28
+ !value.startsWith('//') &&
29
+ !value.startsWith('#') &&
30
+ !value.startsWith('data:');
31
+
32
+ const resolveHtmlAssetPath = (assetSource, sourcePath, manifestDirectory) =>
33
+ path.resolve(
34
+ assetSource.startsWith('/') ? manifestDirectory : path.dirname(sourcePath),
35
+ stripQuery(assetSource).replace(/^\/+/, ''),
36
+ );
37
+
38
+ const toPosixPath = (value) => value.split(path.sep).join(path.posix.sep);
39
+
40
+ const toEntryName = (filePath) =>
41
+ stripQuery(filePath)
42
+ .replace(/\.[^.]+$/, '')
43
+ .replace(/[^a-zA-Z0-9_./-]/g, '_');
44
+
45
+ const toHtmlScriptEntryName = (filePath) =>
46
+ `tap-html-${toEntryName(filePath).replace(/[^a-zA-Z0-9_-]/g, '_')}`;
47
+
48
+ const toHtmlStylesheetEntryName = (filePath) =>
49
+ `tap-html-css-${toEntryName(filePath).replace(/[^a-zA-Z0-9_-]/g, '_')}`;
50
+
51
+ const hashContents = (contents) =>
52
+ crypto.createHash('sha256').update(contents).digest('hex').slice(0, 8);
53
+
54
+ const realpathIfPossible = (filePath) => {
55
+ try {
56
+ return fs.realpathSync.native(filePath);
57
+ } catch {
58
+ return filePath;
59
+ }
60
+ };
61
+
62
+ const findNearestManifestDirectory = (startPath) => {
63
+ let currentDirectory = path.dirname(startPath);
64
+
65
+ while (currentDirectory !== path.dirname(currentDirectory)) {
66
+ if (fs.existsSync(path.join(currentDirectory, 'manifest.tap.json'))) {
67
+ return currentDirectory;
68
+ }
69
+
70
+ currentDirectory = path.dirname(currentDirectory);
71
+ }
72
+
73
+ return undefined;
74
+ };
75
+
76
+ const getLoaderOptions = (loaderContext) => {
77
+ if (typeof loaderContext.getOptions === 'function') {
78
+ const options = loaderContext.getOptions();
79
+
80
+ if (options && Object.keys(options).length > 0) {
81
+ return options;
82
+ }
83
+ }
84
+
85
+ if (typeof loaderContext.query !== 'string') {
86
+ return {};
87
+ }
88
+
89
+ return Object.fromEntries(
90
+ new URLSearchParams(loaderContext.query.replace(/^\?/, '')),
91
+ );
92
+ };
93
+
94
+ module.exports = function tapHtmlLoader(source) {
95
+ const callback = this.async();
96
+ const options = getLoaderOptions(this);
97
+ const sourcePath = realpathIfPossible(this.resourcePath);
98
+ const manifestDirectory = realpathIfPossible(
99
+ options.manifestDirectory ??
100
+ findNearestManifestDirectory(sourcePath) ??
101
+ this.rootContext,
102
+ );
103
+
104
+ const rewrittenScripts = source.replace(
105
+ SCRIPT_SRC_PATTERN,
106
+ (tag, beforeSrc, quote, scriptSource, afterSrc) => {
107
+ if (!isLocalReference(scriptSource)) {
108
+ return tag;
109
+ }
110
+
111
+ const scriptPath = resolveHtmlAssetPath(
112
+ scriptSource,
113
+ sourcePath,
114
+ manifestDirectory,
115
+ );
116
+ const relativeScript = toPosixPath(
117
+ path.relative(manifestDirectory, scriptPath),
118
+ );
119
+ const output = `${SCRIPT_SRC_TOKEN_PREFIX}${toHtmlScriptEntryName(relativeScript)}__`;
120
+
121
+ return `<script${beforeSrc}src=${quote}${output}${quote}${afterSrc}>`;
122
+ },
123
+ );
124
+ const rewrittenHtml = rewrittenScripts.replace(
125
+ STYLESHEET_HREF_PATTERN,
126
+ (tag, _relQuote, beforeHref, hrefQuote, stylesheetSource, afterHref) => {
127
+ if (!isLocalReference(stylesheetSource)) {
128
+ return tag;
129
+ }
130
+
131
+ const stylesheetPath = resolveHtmlAssetPath(
132
+ stylesheetSource,
133
+ sourcePath,
134
+ manifestDirectory,
135
+ );
136
+ const relativeStylesheet = toPosixPath(
137
+ path.relative(manifestDirectory, stylesheetPath),
138
+ );
139
+ const output = `${STYLESHEET_HREF_TOKEN_PREFIX}${toHtmlStylesheetEntryName(relativeStylesheet)}__`;
140
+
141
+ return `<link${beforeHref}href=${hrefQuote}${output}${hrefQuote}${afterHref}>`;
142
+ },
143
+ );
144
+
145
+ const parsedPath = path.parse(sourcePath);
146
+ const outputPath = `${parsedPath.name}.${hashContents(rewrittenHtml)}${parsedPath.ext}`;
147
+
148
+ this.emitFile(outputPath, rewrittenHtml);
149
+ callback(null, `export default ${JSON.stringify(outputPath)};`);
150
+ };