@wp-typia/project-tools 0.22.10 → 0.23.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.
- package/dist/runtime/cli-add-collision.d.ts +25 -0
- package/dist/runtime/cli-add-collision.js +76 -0
- package/dist/runtime/cli-add-help.js +11 -2
- package/dist/runtime/cli-add-kind-ids.d.ts +1 -1
- package/dist/runtime/cli-add-kind-ids.js +3 -0
- package/dist/runtime/cli-add-types.d.ts +117 -0
- package/dist/runtime/cli-add-types.js +26 -0
- package/dist/runtime/cli-add-validation.d.ts +90 -1
- package/dist/runtime/cli-add-validation.js +304 -1
- package/dist/runtime/cli-add-workspace-admin-view-scaffold.js +74 -19
- package/dist/runtime/cli-add-workspace-admin-view-source.js +11 -2
- package/dist/runtime/cli-add-workspace-admin-view-templates.d.ts +20 -2
- package/dist/runtime/cli-add-workspace-admin-view-templates.js +359 -3
- package/dist/runtime/cli-add-workspace-admin-view-types.d.ts +21 -0
- package/dist/runtime/cli-add-workspace-admin-view-types.js +22 -0
- package/dist/runtime/cli-add-workspace-ai-anchors.js +121 -31
- package/dist/runtime/cli-add-workspace-contract-source-emitters.d.ts +15 -0
- package/dist/runtime/cli-add-workspace-contract-source-emitters.js +42 -0
- package/dist/runtime/cli-add-workspace-contract.d.ts +15 -0
- package/dist/runtime/cli-add-workspace-contract.js +65 -0
- package/dist/runtime/cli-add-workspace-integration-env.d.ts +24 -0
- package/dist/runtime/cli-add-workspace-integration-env.js +391 -0
- package/dist/runtime/cli-add-workspace-post-meta-anchors.d.ts +23 -0
- package/dist/runtime/cli-add-workspace-post-meta-anchors.js +244 -0
- package/dist/runtime/cli-add-workspace-post-meta-source-emitters.d.ts +63 -0
- package/dist/runtime/cli-add-workspace-post-meta-source-emitters.js +179 -0
- package/dist/runtime/cli-add-workspace-post-meta.d.ts +15 -0
- package/dist/runtime/cli-add-workspace-post-meta.js +107 -0
- package/dist/runtime/cli-add-workspace-rest-anchors.d.ts +1 -0
- package/dist/runtime/cli-add-workspace-rest-anchors.js +285 -21
- package/dist/runtime/cli-add-workspace-rest-source-emitters.d.ts +90 -2
- package/dist/runtime/cli-add-workspace-rest-source-emitters.js +302 -29
- package/dist/runtime/cli-add-workspace-rest.d.ts +15 -2
- package/dist/runtime/cli-add-workspace-rest.js +329 -21
- package/dist/runtime/cli-add-workspace.d.ts +15 -0
- package/dist/runtime/cli-add-workspace.js +15 -0
- package/dist/runtime/cli-add.d.ts +1 -1
- package/dist/runtime/cli-add.js +1 -1
- package/dist/runtime/cli-core.d.ts +2 -1
- package/dist/runtime/cli-core.js +2 -1
- package/dist/runtime/cli-doctor-environment.js +1 -3
- package/dist/runtime/cli-doctor-workspace-features.js +128 -10
- package/dist/runtime/cli-doctor-workspace-package.d.ts +25 -3
- package/dist/runtime/cli-doctor-workspace-package.js +35 -13
- package/dist/runtime/cli-doctor-workspace-shared.d.ts +2 -0
- package/dist/runtime/cli-doctor-workspace-shared.js +2 -0
- package/dist/runtime/cli-doctor-workspace.js +8 -3
- package/dist/runtime/cli-help.js +7 -0
- package/dist/runtime/cli-init-templates.js +11 -1
- package/dist/runtime/contract-artifacts.d.ts +14 -0
- package/dist/runtime/contract-artifacts.js +15 -0
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/rest-resource-artifacts.d.ts +57 -1
- package/dist/runtime/rest-resource-artifacts.js +97 -1
- package/dist/runtime/template-render.d.ts +1 -1
- package/dist/runtime/template-render.js +1 -1
- package/dist/runtime/template-source-cache-markers.d.ts +37 -0
- package/dist/runtime/template-source-cache-markers.js +125 -0
- package/dist/runtime/template-source-cache.d.ts +1 -4
- package/dist/runtime/template-source-cache.js +16 -122
- package/dist/runtime/template-source-external.d.ts +4 -2
- package/dist/runtime/template-source-external.js +4 -2
- package/dist/runtime/template-source-remote.d.ts +8 -4
- package/dist/runtime/template-source-remote.js +8 -4
- package/dist/runtime/workspace-inventory-mutations.js +52 -3
- package/dist/runtime/workspace-inventory-parser.d.ts +3 -2
- package/dist/runtime/workspace-inventory-parser.js +126 -5
- package/dist/runtime/workspace-inventory-read.d.ts +9 -2
- package/dist/runtime/workspace-inventory-read.js +9 -2
- package/dist/runtime/workspace-inventory-templates.d.ts +16 -1
- package/dist/runtime/workspace-inventory-templates.js +74 -4
- package/dist/runtime/workspace-inventory-types.d.ts +51 -2
- package/dist/runtime/workspace-inventory.d.ts +2 -2
- package/dist/runtime/workspace-inventory.js +1 -1
- package/package.json +2 -2
|
@@ -418,6 +418,345 @@ export async function ${fetchName}(
|
|
|
418
418
|
}
|
|
419
419
|
`;
|
|
420
420
|
}
|
|
421
|
+
/**
|
|
422
|
+
* Build type aliases for a manual REST settings form admin screen.
|
|
423
|
+
*/
|
|
424
|
+
export function buildRestSettingsAdminViewTypesSource(adminViewSlug, restResource) {
|
|
425
|
+
const pascalName = toPascalCase(adminViewSlug);
|
|
426
|
+
const restTypesModule = getAdminViewRelativeModuleSpecifier(adminViewSlug, restResource.typesFile);
|
|
427
|
+
const formStateTypeName = `${pascalName}SettingsFormState`;
|
|
428
|
+
const loadResultTypeName = `${pascalName}SettingsLoadResult`;
|
|
429
|
+
return `import type {
|
|
430
|
+
\t${restResource.bodyTypeName},
|
|
431
|
+
\t${restResource.queryTypeName},
|
|
432
|
+
\t${restResource.responseTypeName},
|
|
433
|
+
} from ${quoteTsString(restTypesModule)};
|
|
434
|
+
|
|
435
|
+
export type ${pascalName}SettingsRequest = ${restResource.bodyTypeName};
|
|
436
|
+
export type ${pascalName}SettingsQuery = ${restResource.queryTypeName};
|
|
437
|
+
export type ${pascalName}SettingsResponse = ${restResource.responseTypeName};
|
|
438
|
+
export type ${formStateTypeName} = Partial<${pascalName}SettingsRequest>;
|
|
439
|
+
|
|
440
|
+
export interface ${loadResultTypeName} {
|
|
441
|
+
\tform: ${formStateTypeName};
|
|
442
|
+
\tresponse: ${pascalName}SettingsResponse | null;
|
|
443
|
+
}
|
|
444
|
+
`;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Build display metadata for a manual REST settings form admin screen.
|
|
448
|
+
*/
|
|
449
|
+
export function buildRestSettingsAdminViewConfigSource(adminViewSlug, textDomain, restResource) {
|
|
450
|
+
const pascalName = toPascalCase(adminViewSlug);
|
|
451
|
+
const camelName = toCamelCase(adminViewSlug);
|
|
452
|
+
const title = toTitleCase(adminViewSlug);
|
|
453
|
+
const configName = `${camelName}SettingsConfig`;
|
|
454
|
+
const formStateTypeName = `${pascalName}SettingsFormState`;
|
|
455
|
+
const secretFieldSource = restResource.secretFieldName && restResource.secretStateFieldName
|
|
456
|
+
? `\t{
|
|
457
|
+
\t\tdescription: __( 'Write-only secret value. Leave blank to keep the existing secret unless your route treats blank values as removal.', ${quoteTsString(textDomain)} ),
|
|
458
|
+
\t\tid: ${quoteTsString(restResource.secretFieldName)},
|
|
459
|
+
\t\tlabel: __( ${quoteTsString(toTitleCase(restResource.secretFieldName))}, ${quoteTsString(textDomain)} ),
|
|
460
|
+
\t\tsecretStateField: ${quoteTsString(restResource.secretStateFieldName)},
|
|
461
|
+
\t\ttype: 'secret',
|
|
462
|
+
\t},`
|
|
463
|
+
: '';
|
|
464
|
+
return `import { __ } from '@wordpress/i18n';
|
|
465
|
+
|
|
466
|
+
import type { ${formStateTypeName} } from './types';
|
|
467
|
+
|
|
468
|
+
export type ${pascalName}SettingsFieldType = 'secret' | 'text' | 'textarea';
|
|
469
|
+
|
|
470
|
+
export interface ${pascalName}SettingsField {
|
|
471
|
+
\tdescription?: string;
|
|
472
|
+
\tid: Extract<keyof ${formStateTypeName}, string> | string;
|
|
473
|
+
\tlabel: string;
|
|
474
|
+
\tsecretStateField?: string;
|
|
475
|
+
\ttype: ${pascalName}SettingsFieldType;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
export const ${configName} = {
|
|
479
|
+
\tdescription: __( 'This generated settings form is backed by the ${restResource.slug} REST contract. Adjust config.ts and data.ts as the contract becomes product-specific.', ${quoteTsString(textDomain)} ),
|
|
480
|
+
\tfields: [
|
|
481
|
+
\t\t{
|
|
482
|
+
\t\t\tdescription: __( 'Primary settings payload for this integration.', ${quoteTsString(textDomain)} ),
|
|
483
|
+
\t\t\tid: 'payload',
|
|
484
|
+
\t\t\tlabel: __( 'Payload', ${quoteTsString(textDomain)} ),
|
|
485
|
+
\t\t\ttype: 'textarea',
|
|
486
|
+
\t\t},
|
|
487
|
+
\t\t{
|
|
488
|
+
\t\t\tdescription: __( 'Optional operator note included with the save request.', ${quoteTsString(textDomain)} ),
|
|
489
|
+
\t\t\tid: 'comment',
|
|
490
|
+
\t\t\tlabel: __( 'Comment', ${quoteTsString(textDomain)} ),
|
|
491
|
+
\t\t\ttype: 'text',
|
|
492
|
+
\t\t},
|
|
493
|
+
${secretFieldSource}
|
|
494
|
+
\t] satisfies ${pascalName}SettingsField[],
|
|
495
|
+
\tsecretFieldName: ${restResource.secretFieldName ? quoteTsString(restResource.secretFieldName) : 'undefined'},
|
|
496
|
+
\tsecretStateFieldName: ${restResource.secretStateFieldName ? quoteTsString(restResource.secretStateFieldName) : 'undefined'},
|
|
497
|
+
\ttitle: __( ${quoteTsString(title)}, ${quoteTsString(textDomain)} ),
|
|
498
|
+
};
|
|
499
|
+
`;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Build data helpers for a manual REST settings form admin screen.
|
|
503
|
+
*/
|
|
504
|
+
export function buildRestSettingsAdminViewDataSource(adminViewSlug, restResource) {
|
|
505
|
+
const pascalName = toPascalCase(adminViewSlug);
|
|
506
|
+
const restApiModule = getAdminViewRelativeModuleSpecifier(adminViewSlug, restResource.apiFile);
|
|
507
|
+
const formStateTypeName = `${pascalName}SettingsFormState`;
|
|
508
|
+
const loadResultTypeName = `${pascalName}SettingsLoadResult`;
|
|
509
|
+
const loadName = `load${pascalName}Settings`;
|
|
510
|
+
const saveName = `save${pascalName}Settings`;
|
|
511
|
+
const initialFields = [
|
|
512
|
+
'\tpayload: \'\',',
|
|
513
|
+
'\tcomment: \'\',',
|
|
514
|
+
].join('\n');
|
|
515
|
+
const requestBodySource = restResource.secretFieldName
|
|
516
|
+
? `\tconst requestBody = { ...form } as Record<string, unknown>;
|
|
517
|
+
\tif (requestBody[${quoteTsString(restResource.secretFieldName)}] === '') {
|
|
518
|
+
\t\tdelete requestBody[${quoteTsString(restResource.secretFieldName)}];
|
|
519
|
+
\t}
|
|
520
|
+
`
|
|
521
|
+
: `\tconst requestBody = form as Record<string, unknown>;
|
|
522
|
+
`;
|
|
523
|
+
return `import { callManualRestContract } from ${quoteTsString(restApiModule)};
|
|
524
|
+
import type {
|
|
525
|
+
\t${formStateTypeName},
|
|
526
|
+
\t${loadResultTypeName},
|
|
527
|
+
\t${pascalName}SettingsQuery,
|
|
528
|
+
\t${pascalName}SettingsRequest,
|
|
529
|
+
\t${pascalName}SettingsResponse,
|
|
530
|
+
} from './types';
|
|
531
|
+
|
|
532
|
+
function formatValidationError(prefix: string, errors: unknown): string {
|
|
533
|
+
\tif (!Array.isArray(errors) || errors.length === 0) {
|
|
534
|
+
\t\treturn prefix;
|
|
535
|
+
\t}
|
|
536
|
+
|
|
537
|
+
\treturn \`\${prefix} \${JSON.stringify(errors)}\`;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export function createInitial${pascalName}SettingsFormState(): ${formStateTypeName} {
|
|
541
|
+
\treturn {
|
|
542
|
+
${initialFields}
|
|
543
|
+
\t} as ${formStateTypeName};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
export async function ${loadName}(): Promise<${loadResultTypeName}> {
|
|
547
|
+
\treturn {
|
|
548
|
+
\t\tform: createInitial${pascalName}SettingsFormState(),
|
|
549
|
+
\t\tresponse: null,
|
|
550
|
+
\t};
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
export async function ${saveName}(
|
|
554
|
+
\tform: ${formStateTypeName},
|
|
555
|
+
\tquery: Partial<${pascalName}SettingsQuery> = {},
|
|
556
|
+
): Promise<${pascalName}SettingsResponse> {
|
|
557
|
+
${requestBodySource}
|
|
558
|
+
\tconst result = await callManualRestContract({
|
|
559
|
+
\t\tbody: requestBody as unknown as ${pascalName}SettingsRequest,
|
|
560
|
+
\t\tquery: query as ${pascalName}SettingsQuery,
|
|
561
|
+
\t});
|
|
562
|
+
\tif (!result.isValid) {
|
|
563
|
+
\t\tconst message =
|
|
564
|
+
\t\t\tresult.validationTarget === 'request'
|
|
565
|
+
\t\t\t\t? 'Settings request failed validation.'
|
|
566
|
+
\t\t\t\t: 'Settings response failed validation.';
|
|
567
|
+
\t\tthrow new Error(formatValidationError(message, result.errors));
|
|
568
|
+
\t}
|
|
569
|
+
|
|
570
|
+
\treturn result.data as ${pascalName}SettingsResponse;
|
|
571
|
+
}
|
|
572
|
+
`;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Build a React settings form screen backed by a manual REST contract.
|
|
576
|
+
*/
|
|
577
|
+
export function buildRestSettingsAdminViewScreenSource(adminViewSlug, textDomain) {
|
|
578
|
+
const pascalName = toPascalCase(adminViewSlug);
|
|
579
|
+
const componentName = `${pascalName}AdminViewScreen`;
|
|
580
|
+
const formStateTypeName = `${pascalName}SettingsFormState`;
|
|
581
|
+
const responseTypeName = `${pascalName}SettingsResponse`;
|
|
582
|
+
const camelName = toCamelCase(adminViewSlug);
|
|
583
|
+
const configName = `${camelName}SettingsConfig`;
|
|
584
|
+
const loadName = `load${pascalName}Settings`;
|
|
585
|
+
const saveName = `save${pascalName}Settings`;
|
|
586
|
+
return `import {
|
|
587
|
+
\tButton,
|
|
588
|
+
\tNotice,
|
|
589
|
+
\tSpinner,
|
|
590
|
+
\tTextControl,
|
|
591
|
+
\tTextareaControl,
|
|
592
|
+
} from '@wordpress/components';
|
|
593
|
+
import { useEffect, useState } from '@wordpress/element';
|
|
594
|
+
import { __ } from '@wordpress/i18n';
|
|
595
|
+
|
|
596
|
+
import { ${configName} } from './config';
|
|
597
|
+
import { ${loadName}, ${saveName} } from './data';
|
|
598
|
+
import type { ${formStateTypeName}, ${responseTypeName} } from './types';
|
|
599
|
+
|
|
600
|
+
function getFieldValue(form: ${formStateTypeName}, fieldId: string): string {
|
|
601
|
+
\tconst value = (form as Record<string, unknown>)[fieldId];
|
|
602
|
+
\tif (typeof value === 'string') {
|
|
603
|
+
\t\treturn value;
|
|
604
|
+
\t}
|
|
605
|
+
\tif (value == null) {
|
|
606
|
+
\t\treturn '';
|
|
607
|
+
\t}
|
|
608
|
+
|
|
609
|
+
\treturn String(value);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function getSecretState(response: ${responseTypeName} | null): boolean | null {
|
|
613
|
+
\tconst stateField = ${configName}.secretStateFieldName;
|
|
614
|
+
\tif (!stateField || !response) {
|
|
615
|
+
\t\treturn null;
|
|
616
|
+
\t}
|
|
617
|
+
|
|
618
|
+
\tconst value = (response as unknown as Record<string, unknown>)[stateField];
|
|
619
|
+
\treturn typeof value === 'boolean' ? value : null;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
export function ${componentName}() {
|
|
623
|
+
\tconst [form, setForm] = useState<${formStateTypeName}>({});
|
|
624
|
+
\tconst [response, setResponse] = useState<${responseTypeName} | null>(null);
|
|
625
|
+
\tconst [error, setError] = useState<string | null>(null);
|
|
626
|
+
\tconst [isLoading, setIsLoading] = useState(true);
|
|
627
|
+
\tconst [isSaving, setIsSaving] = useState(false);
|
|
628
|
+
\tconst [successMessage, setSuccessMessage] = useState<string | null>(null);
|
|
629
|
+
|
|
630
|
+
\tuseEffect(() => {
|
|
631
|
+
\t\tlet isCurrent = true;
|
|
632
|
+
\t\tsetIsLoading(true);
|
|
633
|
+
\t\tsetError(null);
|
|
634
|
+
|
|
635
|
+
\t\tvoid ${loadName}()
|
|
636
|
+
\t\t\t.then((result) => {
|
|
637
|
+
\t\t\t\tif (isCurrent) {
|
|
638
|
+
\t\t\t\t\tsetForm(result.form);
|
|
639
|
+
\t\t\t\t\tsetResponse(result.response);
|
|
640
|
+
\t\t\t\t}
|
|
641
|
+
\t\t\t})
|
|
642
|
+
\t\t\t.catch((nextError: unknown) => {
|
|
643
|
+
\t\t\t\tif (isCurrent) {
|
|
644
|
+
\t\t\t\t\tsetError(
|
|
645
|
+
\t\t\t\t\t\tnextError instanceof Error
|
|
646
|
+
\t\t\t\t\t\t\t? nextError.message
|
|
647
|
+
\t\t\t\t\t\t\t: __( 'Unable to prepare settings form.', ${quoteTsString(textDomain)} ),
|
|
648
|
+
\t\t\t\t\t);
|
|
649
|
+
\t\t\t\t}
|
|
650
|
+
\t\t\t})
|
|
651
|
+
\t\t\t.finally(() => {
|
|
652
|
+
\t\t\t\tif (isCurrent) {
|
|
653
|
+
\t\t\t\t\tsetIsLoading(false);
|
|
654
|
+
\t\t\t\t}
|
|
655
|
+
\t\t\t});
|
|
656
|
+
|
|
657
|
+
\t\treturn () => {
|
|
658
|
+
\t\t\tisCurrent = false;
|
|
659
|
+
\t\t};
|
|
660
|
+
\t}, []);
|
|
661
|
+
|
|
662
|
+
\tconst setFormValue = (fieldId: string, value: string) => {
|
|
663
|
+
\t\tsetForm(
|
|
664
|
+
\t\t\t(current) =>
|
|
665
|
+
\t\t\t\t({
|
|
666
|
+
\t\t\t\t\t...current,
|
|
667
|
+
\t\t\t\t\t[fieldId]: value,
|
|
668
|
+
\t\t\t\t}) as ${formStateTypeName},
|
|
669
|
+
\t\t);
|
|
670
|
+
\t};
|
|
671
|
+
\tconst secretState = getSecretState(response);
|
|
672
|
+
|
|
673
|
+
\tconst handleSubmit = (event: { preventDefault: () => void }) => {
|
|
674
|
+
\t\tevent.preventDefault();
|
|
675
|
+
\t\tsetError(null);
|
|
676
|
+
\t\tsetSuccessMessage(null);
|
|
677
|
+
\t\tsetIsSaving(true);
|
|
678
|
+
|
|
679
|
+
\t\tvoid ${saveName}(form)
|
|
680
|
+
\t\t\t.then((nextResponse) => {
|
|
681
|
+
\t\t\t\tsetResponse(nextResponse);
|
|
682
|
+
\t\t\t\tsetSuccessMessage(__( 'Settings saved.', ${quoteTsString(textDomain)} ));
|
|
683
|
+
\t\t\t})
|
|
684
|
+
\t\t\t.catch((nextError: unknown) => {
|
|
685
|
+
\t\t\t\tsetError(
|
|
686
|
+
\t\t\t\t\tnextError instanceof Error
|
|
687
|
+
\t\t\t\t\t\t? nextError.message
|
|
688
|
+
\t\t\t\t\t\t: __( 'Unable to save settings.', ${quoteTsString(textDomain)} ),
|
|
689
|
+
\t\t\t\t);
|
|
690
|
+
\t\t\t})
|
|
691
|
+
\t\t\t.finally(() => setIsSaving(false));
|
|
692
|
+
\t};
|
|
693
|
+
|
|
694
|
+
\treturn (
|
|
695
|
+
\t\t<div className="wp-typia-admin-view-screen wp-typia-admin-view-screen--settings">
|
|
696
|
+
\t\t\t<header className="wp-typia-admin-view-screen__header">
|
|
697
|
+
\t\t\t\t<div>
|
|
698
|
+
\t\t\t\t\t<p className="wp-typia-admin-view-screen__eyebrow">
|
|
699
|
+
\t\t\t\t\t\t{ __( 'Typed settings screen', ${quoteTsString(textDomain)} ) }
|
|
700
|
+
\t\t\t\t\t</p>
|
|
701
|
+
\t\t\t\t\t<h1>{ ${configName}.title }</h1>
|
|
702
|
+
\t\t\t\t\t<p>{ ${configName}.description }</p>
|
|
703
|
+
\t\t\t\t</div>
|
|
704
|
+
\t\t\t\t<div className="wp-typia-admin-view-screen__actions">
|
|
705
|
+
\t\t\t\t\t{ isLoading || isSaving ? <Spinner /> : null }
|
|
706
|
+
\t\t\t\t</div>
|
|
707
|
+
\t\t\t</header>
|
|
708
|
+
\t\t\t{ error ? (
|
|
709
|
+
\t\t\t\t<Notice isDismissible={ false } status="error">
|
|
710
|
+
\t\t\t\t\t{ error }
|
|
711
|
+
\t\t\t\t</Notice>
|
|
712
|
+
\t\t\t) : null }
|
|
713
|
+
\t\t\t{ successMessage ? (
|
|
714
|
+
\t\t\t\t<Notice isDismissible={ false } status="success">
|
|
715
|
+
\t\t\t\t\t{ successMessage }
|
|
716
|
+
\t\t\t\t</Notice>
|
|
717
|
+
\t\t\t) : null }
|
|
718
|
+
\t\t\t{ secretState !== null ? (
|
|
719
|
+
\t\t\t\t<Notice isDismissible={ false } status="info">
|
|
720
|
+
\t\t\t\t\t{ secretState
|
|
721
|
+
\t\t\t\t\t\t? __( 'A secret is currently configured for this integration.', ${quoteTsString(textDomain)} )
|
|
722
|
+
\t\t\t\t\t\t: __( 'No secret is currently configured for this integration.', ${quoteTsString(textDomain)} ) }
|
|
723
|
+
\t\t\t\t</Notice>
|
|
724
|
+
\t\t\t) : null }
|
|
725
|
+
\t\t\t<form className="wp-typia-admin-view-screen__settings-form" onSubmit={ handleSubmit }>
|
|
726
|
+
\t\t\t\t{ ${configName}.fields.map((field) => (
|
|
727
|
+
\t\t\t\t\t<div className="wp-typia-admin-view-screen__field" key={ field.id }>
|
|
728
|
+
\t\t\t\t\t\t{ field.type === 'textarea' ? (
|
|
729
|
+
\t\t\t\t\t\t\t<TextareaControl
|
|
730
|
+
\t\t\t\t\t\t\t\thelp={ field.description }
|
|
731
|
+
\t\t\t\t\t\t\t\tlabel={ field.label }
|
|
732
|
+
\t\t\t\t\t\t\t\tonChange={ (value) => setFormValue(field.id, value) }
|
|
733
|
+
\t\t\t\t\t\t\t\tvalue={ getFieldValue(form, field.id) }
|
|
734
|
+
\t\t\t\t\t\t\t/>
|
|
735
|
+
\t\t\t\t\t\t) : (
|
|
736
|
+
\t\t\t\t\t\t\t<TextControl
|
|
737
|
+
\t\t\t\t\t\t\t\thelp={ field.description }
|
|
738
|
+
\t\t\t\t\t\t\t\tlabel={ field.label }
|
|
739
|
+
\t\t\t\t\t\t\t\tonChange={ (value) => setFormValue(field.id, value) }
|
|
740
|
+
\t\t\t\t\t\t\t\ttype={ field.type === 'secret' ? 'password' : 'text' }
|
|
741
|
+
\t\t\t\t\t\t\t\tvalue={ getFieldValue(form, field.id) }
|
|
742
|
+
\t\t\t\t\t\t\t/>
|
|
743
|
+
\t\t\t\t\t\t) }
|
|
744
|
+
\t\t\t\t\t</div>
|
|
745
|
+
\t\t\t\t)) }
|
|
746
|
+
\t\t\t\t<Button
|
|
747
|
+
\t\t\t\t\tdisabled={ isLoading || isSaving }
|
|
748
|
+
\t\t\t\t\tisBusy={ isSaving }
|
|
749
|
+
\t\t\t\t\ttype="submit"
|
|
750
|
+
\t\t\t\t\tvariant="primary"
|
|
751
|
+
\t\t\t\t>
|
|
752
|
+
\t\t\t\t\t{ __( 'Save settings', ${quoteTsString(textDomain)} ) }
|
|
753
|
+
\t\t\t\t</Button>
|
|
754
|
+
\t\t\t</form>
|
|
755
|
+
\t\t</div>
|
|
756
|
+
\t);
|
|
757
|
+
}
|
|
758
|
+
`;
|
|
759
|
+
}
|
|
421
760
|
/**
|
|
422
761
|
* Build a core-data-backed admin-view data module for a supported entity family.
|
|
423
762
|
*/
|
|
@@ -820,13 +1159,15 @@ export function ${componentName}() {
|
|
|
820
1159
|
}
|
|
821
1160
|
`;
|
|
822
1161
|
}
|
|
823
|
-
export function buildAdminViewEntrySource(adminViewSlug) {
|
|
1162
|
+
export function buildAdminViewEntrySource(adminViewSlug, options = {}) {
|
|
824
1163
|
const pascalName = toPascalCase(adminViewSlug);
|
|
825
1164
|
const componentName = `${pascalName}AdminViewScreen`;
|
|
826
1165
|
const rootId = `wp-typia-admin-view-${adminViewSlug}`;
|
|
1166
|
+
const dataViewsStyleImport = options.includeDataViewsStyle === false
|
|
1167
|
+
? ''
|
|
1168
|
+
: "\nimport '@wordpress/dataviews/build-style/style.css';";
|
|
827
1169
|
return `import { createRoot } from '@wordpress/element';
|
|
828
|
-
|
|
829
|
-
import '@wordpress/dataviews/build-style/style.css';
|
|
1170
|
+
${dataViewsStyleImport}
|
|
830
1171
|
import { ${componentName} } from './Screen';
|
|
831
1172
|
import './style.scss';
|
|
832
1173
|
|
|
@@ -887,6 +1228,21 @@ export function buildAdminViewStyleSource() {
|
|
|
887
1228
|
\tdisplay: flex;
|
|
888
1229
|
\tgap: 12px;
|
|
889
1230
|
}
|
|
1231
|
+
|
|
1232
|
+
.wp-typia-admin-view-screen__settings-form {
|
|
1233
|
+
\tbackground: #fff;
|
|
1234
|
+
\tborder: 1px solid #dcdcde;
|
|
1235
|
+
\tborder-radius: 2px;
|
|
1236
|
+
\tbox-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
|
|
1237
|
+
\tdisplay: grid;
|
|
1238
|
+
\tgap: 20px;
|
|
1239
|
+
\tmax-width: 720px;
|
|
1240
|
+
\tpadding: 24px;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
.wp-typia-admin-view-screen__field .components-base-control {
|
|
1244
|
+
\tmargin-bottom: 0;
|
|
1245
|
+
}
|
|
890
1246
|
`;
|
|
891
1247
|
}
|
|
892
1248
|
export function buildAdminViewPhpSource(adminViewSlug, workspace) {
|
|
@@ -22,6 +22,27 @@ export interface AdminViewCoreDataSource {
|
|
|
22
22
|
}
|
|
23
23
|
export type AdminViewSource = AdminViewCoreDataSource | AdminViewRestResourceSource;
|
|
24
24
|
export type AdminViewRestResource = WorkspaceRestResourceInventoryEntry;
|
|
25
|
+
/**
|
|
26
|
+
* Manual REST resource metadata required to scaffold a typed admin settings
|
|
27
|
+
* screen. The type narrows a workspace REST inventory entry to manual
|
|
28
|
+
* contracts that expose request body, query, and response type names.
|
|
29
|
+
*/
|
|
30
|
+
export type AdminViewManualSettingsRestResource = AdminViewRestResource & {
|
|
31
|
+
bodyTypeName: string;
|
|
32
|
+
mode: 'manual';
|
|
33
|
+
queryTypeName: string;
|
|
34
|
+
responseTypeName: string;
|
|
35
|
+
};
|
|
25
36
|
export declare function isAdminViewCoreDataSource(source: AdminViewSource | undefined): source is AdminViewCoreDataSource;
|
|
26
37
|
export declare function isAdminViewRestResourceSource(source: AdminViewSource | undefined): source is AdminViewRestResourceSource;
|
|
38
|
+
/**
|
|
39
|
+
* Return whether a REST inventory entry has the manual contract shape required
|
|
40
|
+
* by generated settings screens.
|
|
41
|
+
*/
|
|
42
|
+
export declare function isAdminViewManualSettingsRestResource(restResource: AdminViewRestResource | undefined): restResource is AdminViewManualSettingsRestResource;
|
|
43
|
+
/**
|
|
44
|
+
* Return whether a REST inventory entry uses route regex groups that a
|
|
45
|
+
* generated singleton settings screen cannot satisfy with path input.
|
|
46
|
+
*/
|
|
47
|
+
export declare function hasAdminViewManualSettingsRouteParameters(restResource: AdminViewRestResource | undefined): boolean;
|
|
27
48
|
export declare function formatAdminViewSourceLocator(source: AdminViewSource): string;
|
|
@@ -12,12 +12,34 @@ export const ADMIN_VIEWS_ASSET = 'build/admin-views/index.asset.php';
|
|
|
12
12
|
export const ADMIN_VIEWS_STYLE = 'build/admin-views/style-index.css';
|
|
13
13
|
export const ADMIN_VIEWS_STYLE_RTL = 'build/admin-views/style-index-rtl.css';
|
|
14
14
|
export const ADMIN_VIEWS_PHP_GLOB = '/inc/admin-views/*.php';
|
|
15
|
+
const ADMIN_VIEW_MANUAL_REST_ROUTE_PARAMETER_PATTERN = /(?:^|[^\\])\(/u;
|
|
15
16
|
export function isAdminViewCoreDataSource(source) {
|
|
16
17
|
return source?.kind === ADMIN_VIEW_CORE_DATA_SOURCE_KIND;
|
|
17
18
|
}
|
|
18
19
|
export function isAdminViewRestResourceSource(source) {
|
|
19
20
|
return source?.kind === ADMIN_VIEW_REST_SOURCE_KIND;
|
|
20
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Return whether a REST inventory entry has the manual contract shape required
|
|
24
|
+
* by generated settings screens.
|
|
25
|
+
*/
|
|
26
|
+
export function isAdminViewManualSettingsRestResource(restResource) {
|
|
27
|
+
return (restResource?.mode === 'manual' &&
|
|
28
|
+
typeof restResource.bodyTypeName === 'string' &&
|
|
29
|
+
restResource.bodyTypeName.trim().length > 0 &&
|
|
30
|
+
typeof restResource.queryTypeName === 'string' &&
|
|
31
|
+
restResource.queryTypeName.trim().length > 0 &&
|
|
32
|
+
typeof restResource.responseTypeName === 'string' &&
|
|
33
|
+
restResource.responseTypeName.trim().length > 0);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Return whether a REST inventory entry uses route regex groups that a
|
|
37
|
+
* generated singleton settings screen cannot satisfy with path input.
|
|
38
|
+
*/
|
|
39
|
+
export function hasAdminViewManualSettingsRouteParameters(restResource) {
|
|
40
|
+
return [restResource?.pathPattern, restResource?.routePattern].some((pattern) => typeof pattern === 'string' &&
|
|
41
|
+
ADMIN_VIEW_MANUAL_REST_ROUTE_PARAMETER_PATTERN.test(pattern));
|
|
42
|
+
}
|
|
21
43
|
export function formatAdminViewSourceLocator(source) {
|
|
22
44
|
if (isAdminViewCoreDataSource(source)) {
|
|
23
45
|
return `${source.kind}:${source.entityKind}/${source.entityName}`;
|
|
@@ -126,35 +126,131 @@ function replaceRequiredSyncRestSource(nextSource, target, anchor, replacement,
|
|
|
126
126
|
assertSyncRestAnchor(nextSource, target, anchorDescription, hasAnchor, syncRestScriptPath);
|
|
127
127
|
return nextSource.replace(anchor, replacement);
|
|
128
128
|
}
|
|
129
|
+
function replaceBlockConfigImportForAiFeatures(nextSource, syncRestScriptPath) {
|
|
130
|
+
const importPatterns = [
|
|
131
|
+
/^import\s*\{\n(?:\t[^\n]*\n)+\} from ["']\.\/block-config["'];?$/mu,
|
|
132
|
+
/^import\s*\{[^\n]*\}\s*from\s*["']\.\/block-config["'];?$/mu,
|
|
133
|
+
];
|
|
134
|
+
const importMatch = importPatterns.map((pattern) => pattern.exec(nextSource)).find(Boolean) ??
|
|
135
|
+
null;
|
|
136
|
+
if (!importMatch) {
|
|
137
|
+
throw new Error([
|
|
138
|
+
`ensureAiFeatureSyncRestAnchors could not patch ${path.basename(syncRestScriptPath)}.`,
|
|
139
|
+
"Missing expected workspace inventory import anchor in scripts/sync-rest-contracts.ts.",
|
|
140
|
+
"Restore the generated template or add the AI feature wiring manually before retrying.",
|
|
141
|
+
].join(" "));
|
|
142
|
+
}
|
|
143
|
+
const importSource = importMatch[0];
|
|
144
|
+
if (importSource.includes("AI_FEATURES") &&
|
|
145
|
+
importSource.includes("WorkspaceAiFeatureConfig")) {
|
|
146
|
+
return nextSource;
|
|
147
|
+
}
|
|
148
|
+
const hasContracts = importSource.includes("CONTRACTS");
|
|
149
|
+
const hasContractConfig = importSource.includes("WorkspaceContractConfig");
|
|
150
|
+
const hasPostMeta = importSource.includes("POST_META");
|
|
151
|
+
const hasPostMetaConfig = importSource.includes("WorkspacePostMetaConfig");
|
|
152
|
+
const replacement = [
|
|
153
|
+
"import {",
|
|
154
|
+
"\tAI_FEATURES,",
|
|
155
|
+
"\tBLOCKS,",
|
|
156
|
+
...(hasContracts ? ["\tCONTRACTS,"] : []),
|
|
157
|
+
...(hasPostMeta ? ["\tPOST_META,"] : []),
|
|
158
|
+
"\tREST_RESOURCES,",
|
|
159
|
+
"\ttype WorkspaceAiFeatureConfig,",
|
|
160
|
+
"\ttype WorkspaceBlockConfig,",
|
|
161
|
+
...(hasContractConfig ? ["\ttype WorkspaceContractConfig,"] : []),
|
|
162
|
+
...(hasPostMetaConfig ? ["\ttype WorkspacePostMetaConfig,"] : []),
|
|
163
|
+
"\ttype WorkspaceRestResourceConfig,",
|
|
164
|
+
"} from './block-config';",
|
|
165
|
+
].join("\n");
|
|
166
|
+
return nextSource.replace(importSource, replacement);
|
|
167
|
+
}
|
|
168
|
+
function replaceAiFeatureSyncSummaryCopy(nextSource, syncRestScriptPath) {
|
|
169
|
+
const standaloneSummary = "workspace blocks, standalone contracts, and plugin-level resources";
|
|
170
|
+
const standaloneAiSummary = "workspace blocks, standalone contracts, plugin-level resources, and AI features";
|
|
171
|
+
const standalonePostMetaSummary = "workspace blocks, standalone contracts, post meta contracts, and plugin-level resources";
|
|
172
|
+
const standalonePostMetaAiSummary = "workspace blocks, standalone contracts, post meta contracts, plugin-level resources, and AI features";
|
|
173
|
+
const postMetaSummary = "workspace blocks, post meta contracts, and plugin-level resources";
|
|
174
|
+
const postMetaAiSummary = "workspace blocks, post meta contracts, plugin-level resources, and AI features";
|
|
175
|
+
const restResourceSummary = "workspace blocks and plugin-level resources";
|
|
176
|
+
const restResourceAiSummary = "workspace blocks, plugin-level resources, and AI features";
|
|
177
|
+
if (nextSource.includes(standalonePostMetaSummary)) {
|
|
178
|
+
return nextSource
|
|
179
|
+
.split(standalonePostMetaSummary)
|
|
180
|
+
.join(standalonePostMetaAiSummary);
|
|
181
|
+
}
|
|
182
|
+
if (nextSource.includes(standaloneSummary)) {
|
|
183
|
+
return nextSource.split(standaloneSummary).join(standaloneAiSummary);
|
|
184
|
+
}
|
|
185
|
+
if (nextSource.includes(postMetaSummary)) {
|
|
186
|
+
return nextSource.split(postMetaSummary).join(postMetaAiSummary);
|
|
187
|
+
}
|
|
188
|
+
if (nextSource.includes(restResourceSummary)) {
|
|
189
|
+
return nextSource.split(restResourceSummary).join(restResourceAiSummary);
|
|
190
|
+
}
|
|
191
|
+
if (nextSource.includes(standaloneAiSummary) ||
|
|
192
|
+
nextSource.includes(standalonePostMetaAiSummary) ||
|
|
193
|
+
nextSource.includes(postMetaAiSummary) ||
|
|
194
|
+
nextSource.includes(restResourceAiSummary)) {
|
|
195
|
+
return nextSource;
|
|
196
|
+
}
|
|
197
|
+
throw new Error([
|
|
198
|
+
`ensureAiFeatureSyncRestAnchors could not patch ${path.basename(syncRestScriptPath)}.`,
|
|
199
|
+
"Missing expected sync summary copy anchor in scripts/sync-rest-contracts.ts.",
|
|
200
|
+
"Restore the generated template or add the AI feature wiring manually before retrying.",
|
|
201
|
+
].join(" "));
|
|
202
|
+
}
|
|
203
|
+
function formatNoResourcesSubject(subjects) {
|
|
204
|
+
if (subjects.length <= 2) {
|
|
205
|
+
return subjects.join(" or ");
|
|
206
|
+
}
|
|
207
|
+
const lastSubject = subjects[subjects.length - 1];
|
|
208
|
+
return `${subjects.slice(0, -1).join(", ")}, or ${lastSubject}`;
|
|
209
|
+
}
|
|
210
|
+
function buildAiFeatureNoResourcesGuard({ hasPostMeta, hasStandaloneContracts, }) {
|
|
211
|
+
const condition = ["restBlocks.length === 0"];
|
|
212
|
+
if (hasStandaloneContracts) {
|
|
213
|
+
condition[condition.length - 1] = `${condition[condition.length - 1]} &&`;
|
|
214
|
+
condition.push("standaloneContracts.length === 0");
|
|
215
|
+
}
|
|
216
|
+
if (hasPostMeta) {
|
|
217
|
+
condition[condition.length - 1] = `${condition[condition.length - 1]} &&`;
|
|
218
|
+
condition.push("postMetaContracts.length === 0");
|
|
219
|
+
}
|
|
220
|
+
condition[condition.length - 1] = `${condition[condition.length - 1]} &&`;
|
|
221
|
+
condition.push("restResources.length === 0 &&");
|
|
222
|
+
condition.push("aiFeatures.length === 0");
|
|
223
|
+
const noResourcesSubject = formatNoResourcesSubject([
|
|
224
|
+
"REST-enabled workspace blocks",
|
|
225
|
+
...(hasStandaloneContracts ? ["standalone contracts"] : []),
|
|
226
|
+
...(hasPostMeta ? ["post meta contracts"] : []),
|
|
227
|
+
"plugin-level REST resources",
|
|
228
|
+
"AI features",
|
|
229
|
+
]);
|
|
230
|
+
return [
|
|
231
|
+
"if (",
|
|
232
|
+
...condition.map((line) => `\t\t${line}`),
|
|
233
|
+
"\t) {",
|
|
234
|
+
"\t\tconsole.log(",
|
|
235
|
+
"\t\t\toptions.check",
|
|
236
|
+
`\t\t\t\t? 'ℹ️ No ${noResourcesSubject} are registered yet. \`sync-rest --check\` is already clean.'`,
|
|
237
|
+
`\t\t\t\t: 'ℹ️ No ${noResourcesSubject} are registered yet.'`,
|
|
238
|
+
"\t\t);",
|
|
239
|
+
"\t\treturn;",
|
|
240
|
+
"\t}",
|
|
241
|
+
].join("\n");
|
|
242
|
+
}
|
|
243
|
+
const NO_RESOURCES_GUARD_PATTERN = /if \(\s*restBlocks\.length === 0(?:\s*&&\s*standaloneContracts\.length === 0)?(?:\s*&&\s*postMetaContracts\.length === 0)?\s*&&\s*restResources\.length === 0(?:\s*&&\s*aiFeatures\.length === 0)?\s*\) \{[\s\S]*?\n\t\treturn;\n\t\}/u;
|
|
129
244
|
/**
|
|
130
245
|
* Patch `scripts/sync-rest-contracts.ts` after sync-project wiring so AI feature REST artifacts join the split sync flow.
|
|
131
246
|
*/
|
|
132
247
|
export async function ensureAiFeatureSyncRestAnchors(workspace) {
|
|
133
248
|
const syncRestScriptPath = path.join(workspace.projectDir, "scripts", "sync-rest-contracts.ts");
|
|
134
249
|
await patchFile(syncRestScriptPath, (source) => {
|
|
135
|
-
let nextSource = source;
|
|
136
|
-
const importAnchor = [
|
|
137
|
-
"import {",
|
|
138
|
-
"\tBLOCKS,",
|
|
139
|
-
"\tREST_RESOURCES,",
|
|
140
|
-
"\ttype WorkspaceBlockConfig,",
|
|
141
|
-
"\ttype WorkspaceRestResourceConfig,",
|
|
142
|
-
"} from './block-config';",
|
|
143
|
-
].join("\n");
|
|
250
|
+
let nextSource = replaceBlockConfigImportForAiFeatures(source, syncRestScriptPath);
|
|
144
251
|
const helperInsertionAnchor = "async function assertTypeArtifactsCurrent";
|
|
145
252
|
const restResourcesAnchor = "const restResources = REST_RESOURCES.filter( isWorkspaceRestResource );";
|
|
146
|
-
const noResourcesPattern = /if \( restBlocks.length === 0 && restResources.length === 0 \) \{[\s\S]*?\n\t\treturn;\n\t\}/u;
|
|
147
253
|
const consoleLogPattern = /\n\tconsole\.log\(\n\t\toptions\.check/u;
|
|
148
|
-
nextSource = replaceRequiredSyncRestSource(nextSource, "AI_FEATURES", importAnchor, [
|
|
149
|
-
"import {",
|
|
150
|
-
"\tAI_FEATURES,",
|
|
151
|
-
"\tBLOCKS,",
|
|
152
|
-
"\tREST_RESOURCES,",
|
|
153
|
-
"\ttype WorkspaceAiFeatureConfig,",
|
|
154
|
-
"\ttype WorkspaceBlockConfig,",
|
|
155
|
-
"\ttype WorkspaceRestResourceConfig,",
|
|
156
|
-
"} from './block-config';",
|
|
157
|
-
].join("\n"), "workspace inventory import", syncRestScriptPath);
|
|
158
254
|
nextSource = replaceRequiredSyncRestSource(nextSource, "function isWorkspaceAiFeature(", helperInsertionAnchor, [
|
|
159
255
|
"function isWorkspaceAiFeature(",
|
|
160
256
|
"\tfeature: WorkspaceAiFeatureConfig",
|
|
@@ -183,16 +279,10 @@ export async function ensureAiFeatureSyncRestAnchors(workspace) {
|
|
|
183
279
|
"const restResources = REST_RESOURCES.filter( isWorkspaceRestResource );",
|
|
184
280
|
"const aiFeatures = AI_FEATURES.filter( isWorkspaceAiFeature );",
|
|
185
281
|
].join("\n"), "rest resource filter", syncRestScriptPath);
|
|
186
|
-
nextSource = replaceRequiredSyncRestSource(nextSource, "
|
|
187
|
-
|
|
188
|
-
"
|
|
189
|
-
|
|
190
|
-
"\t\t\t\t? 'ℹ️ No REST-enabled workspace blocks, plugin-level REST resources, or AI features are registered yet. `sync-rest --check` is already clean.'",
|
|
191
|
-
"\t\t\t\t: 'ℹ️ No REST-enabled workspace blocks, plugin-level REST resources, or AI features are registered yet.'",
|
|
192
|
-
"\t\t);",
|
|
193
|
-
"\t\treturn;",
|
|
194
|
-
"\t}",
|
|
195
|
-
].join("\n"), "no-resources guard", syncRestScriptPath);
|
|
282
|
+
nextSource = replaceRequiredSyncRestSource(nextSource, "aiFeatures.length === 0", NO_RESOURCES_GUARD_PATTERN, buildAiFeatureNoResourcesGuard({
|
|
283
|
+
hasPostMeta: nextSource.includes("const postMetaContracts = POST_META.filter( isWorkspacePostMetaContract );"),
|
|
284
|
+
hasStandaloneContracts: nextSource.includes("const standaloneContracts = CONTRACTS.filter( isWorkspaceStandaloneContract );"),
|
|
285
|
+
}), "no-resources guard", syncRestScriptPath);
|
|
196
286
|
nextSource = replaceRequiredSyncRestSource(nextSource, "for ( const feature of aiFeatures ) {", consoleLogPattern, [
|
|
197
287
|
"",
|
|
198
288
|
"\tfor ( const feature of aiFeatures ) {",
|
|
@@ -247,7 +337,7 @@ export async function ensureAiFeatureSyncRestAnchors(workspace) {
|
|
|
247
337
|
"\tconsole.log(",
|
|
248
338
|
"\t\toptions.check",
|
|
249
339
|
].join("\n"), "final sync summary", syncRestScriptPath);
|
|
250
|
-
nextSource =
|
|
340
|
+
nextSource = replaceAiFeatureSyncSummaryCopy(nextSource, syncRestScriptPath);
|
|
251
341
|
return nextSource;
|
|
252
342
|
});
|
|
253
343
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render one `CONTRACTS` inventory entry for `scripts/block-config.ts`.
|
|
3
|
+
*
|
|
4
|
+
* @param contractSlug Stable kebab-case contract id.
|
|
5
|
+
* @param sourceTypeName Exported TypeScript type/interface used for schema generation.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildContractConfigEntry(contractSlug: string, sourceTypeName: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Render a small starter TypeScript contract that users can replace with their
|
|
10
|
+
* real external route, smoke-test, or PHP integration payload shape.
|
|
11
|
+
*
|
|
12
|
+
* @param contractSlug Stable kebab-case contract id.
|
|
13
|
+
* @param sourceTypeName Exported TypeScript type/interface used for schema generation.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildContractTypesSource(contractSlug: string, sourceTypeName: string): string;
|