extension-create 4.1.18 → 4.1.19
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/README.md +1 -1
- package/dist/lib/messages.d.ts +1 -0
- package/dist/module.cjs +38 -3
- package/dist/steps/import-external-template.d.ts +9 -0
- package/dist/test-template-javascript/.extension-create.json +1 -1
- package/dist/test-template-javascript/STORE.md +1 -1
- package/dist/test-template-javascript/package.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Creates a new extension project with the specified configuration.
|
|
|
47
47
|
|
|
48
48
|
- `projectName` (string, required) - The name of your extension project
|
|
49
49
|
- `options` (object) - Configuration options
|
|
50
|
-
- `template` (string, optional) - Template name or URL. Defaults to `'javascript'` (`init` is an alias)
|
|
50
|
+
- `template` (string, optional) - Template name or URL. Defaults to `'javascript'` (`init` is an alias). A URL must use `https://`. A plain `http://` URL, and an `http://` value in `EXTENSION_CREATE_TEMPLATE_URL`, fails unless `EXTENSION_ALLOW_HTTP_TEMPLATE=true` is set
|
|
51
51
|
- `install` (boolean, optional) - Whether to install dependencies after scaffolding. Defaults to `false` so project creation is fast and users see the familiar `npm install` output on their own.
|
|
52
52
|
- `cliVersion` (string, optional) - CLI version for package.json
|
|
53
53
|
|
package/dist/lib/messages.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare function writingTypeDefinitionsError(error: unknown): string;
|
|
|
11
11
|
export declare function installingFromTemplate(projectName: string, templateName: string): string;
|
|
12
12
|
export declare function usingTemplate(templateName: string, source: string): string;
|
|
13
13
|
export declare function installingFromTemplateError(template: string, error: unknown): string;
|
|
14
|
+
export declare function templateUrlNotHttps(url: string): string;
|
|
14
15
|
export declare function templateFetchTimedOut(templateName: string, ms: number): string;
|
|
15
16
|
export declare function templateNotFoundInCatalog(templateName: string, error?: unknown): string;
|
|
16
17
|
export declare function templateDownloadFailed(templateName: string, error: unknown): string;
|
package/dist/module.cjs
CHANGED
|
@@ -252,6 +252,9 @@ function usingTemplate(templateName, source) {
|
|
|
252
252
|
function installingFromTemplateError(template, error) {
|
|
253
253
|
return `${prefix('error')} Couldn't find the template ${external_pintor_default().blue(template)}.\n${fmt.label('REASON')} ${fmt.val(fmt.truncate(String(error)))}\n${external_pintor_default().red('Choose a template name from')} ${external_pintor_default().blue('extension create --help')}${external_pintor_default().red(', or pass a GitHub URL.')}`;
|
|
254
254
|
}
|
|
255
|
+
function templateUrlNotHttps(url) {
|
|
256
|
+
return `${prefix('error')} Can't download a template over plain HTTP.\n${fmt.label('GOT')} ${fmt.val(fmt.truncate(url, 120))}\n${external_pintor_default().red('- Use the')} ${external_pintor_default().blue('https://')} ${external_pintor_default().red('address of the template.')}\n${external_pintor_default().red('- Set')} ${external_pintor_default().blue('EXTENSION_ALLOW_HTTP_TEMPLATE=true')} ${external_pintor_default().red('to allow plain HTTP.')}`;
|
|
257
|
+
}
|
|
255
258
|
function templateFetchTimedOut(templateName, ms) {
|
|
256
259
|
return `${prefix('error')} Couldn't fetch the template ${external_pintor_default().blue(templateName)} within ${Math.round(ms / 1000)}s.\n${external_pintor_default().red('- Check your network connection.')}\n${external_pintor_default().red('- Set')} ${external_pintor_default().blue('EXTENSION_CREATE_TIMEOUT_MS')} ${external_pintor_default().red('to allow more time.')}`;
|
|
257
260
|
}
|
|
@@ -655,6 +658,29 @@ class TemplateNotFoundError extends Error {
|
|
|
655
658
|
if (cause) this.cause = cause;
|
|
656
659
|
}
|
|
657
660
|
}
|
|
661
|
+
class InsecureTemplateUrlError extends Error {
|
|
662
|
+
constructor(url){
|
|
663
|
+
super(`template URL is not https: ${url}`), _define_property(this, "url", void 0);
|
|
664
|
+
this.name = 'InsecureTemplateUrlError';
|
|
665
|
+
this.url = url;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function isRefusedHttpTemplateUrl(url) {
|
|
669
|
+
if ('true' === process.env.EXTENSION_ALLOW_HTTP_TEMPLATE) return false;
|
|
670
|
+
return /^http:\/\//i.test(url);
|
|
671
|
+
}
|
|
672
|
+
function refuseHttpRedirect(options) {
|
|
673
|
+
const target = String(options.href || `${options.protocol || ''}//`);
|
|
674
|
+
if (isRefusedHttpTemplateUrl(target)) throw new InsecureTemplateUrlError(target);
|
|
675
|
+
}
|
|
676
|
+
function findInsecureTemplateUrlError(error) {
|
|
677
|
+
let current = error;
|
|
678
|
+
for(let depth = 0; current && depth < 4; depth++){
|
|
679
|
+
if (current instanceof InsecureTemplateUrlError) return current;
|
|
680
|
+
current = current.cause;
|
|
681
|
+
}
|
|
682
|
+
return null;
|
|
683
|
+
}
|
|
658
684
|
class TemplateDownloadError extends Error {
|
|
659
685
|
constructor(templateName, cause){
|
|
660
686
|
const msg = cause?.message ?? String(cause);
|
|
@@ -674,11 +700,13 @@ async function downloadArchive(url, timeoutMs, attempts = 2) {
|
|
|
674
700
|
timeout: timeoutMs,
|
|
675
701
|
headers: {
|
|
676
702
|
'User-Agent': 'extension-create'
|
|
677
|
-
}
|
|
703
|
+
},
|
|
704
|
+
beforeRedirect: refuseHttpRedirect
|
|
678
705
|
});
|
|
679
706
|
return Buffer.from(data);
|
|
680
707
|
} catch (error) {
|
|
681
708
|
lastError = error;
|
|
709
|
+
if (findInsecureTemplateUrlError(error)) break;
|
|
682
710
|
const status = error?.response?.status;
|
|
683
711
|
const retriable = void 0 === status || 429 === status || status >= 500;
|
|
684
712
|
if (attempt >= attempts || !retriable) break;
|
|
@@ -712,6 +740,7 @@ async function extractExamplesTemplateFromZip(zipBuffer, templateName, projectPa
|
|
|
712
740
|
async function importFromExamplesCatalog(templateName, projectPath) {
|
|
713
741
|
const ref = process.env.EXTENSION_CREATE_TEMPLATE_REF || DEFAULT_TEMPLATES_REF;
|
|
714
742
|
const overrideUrl = process.env.EXTENSION_CREATE_TEMPLATE_URL || void 0;
|
|
743
|
+
if (overrideUrl && isRefusedHttpTemplateUrl(overrideUrl)) throw new InsecureTemplateUrlError(overrideUrl);
|
|
715
744
|
const urls = resolveCatalogUrls(ref, overrideUrl);
|
|
716
745
|
let buffer;
|
|
717
746
|
let source;
|
|
@@ -721,6 +750,8 @@ async function importFromExamplesCatalog(templateName, projectPath) {
|
|
|
721
750
|
source = candidate;
|
|
722
751
|
break;
|
|
723
752
|
} catch (error) {
|
|
753
|
+
const insecure = findInsecureTemplateUrlError(error);
|
|
754
|
+
if (insecure) throw insecure;
|
|
724
755
|
lastError = error;
|
|
725
756
|
}
|
|
726
757
|
if (!buffer || !source) throw new TemplateDownloadError(templateName, lastError);
|
|
@@ -884,6 +915,7 @@ async function importExternalTemplate(projectPath, projectName, template, logger
|
|
|
884
915
|
} catch {}
|
|
885
916
|
const ownerGitignore = dirExistedBeforeImport ? await readOwnerGitignore(projectPath) : null;
|
|
886
917
|
try {
|
|
918
|
+
if (isRefusedHttpTemplateUrl(template)) throw new InsecureTemplateUrlError(template);
|
|
887
919
|
await promises_namespaceObject.mkdir(projectPath, {
|
|
888
920
|
recursive: true
|
|
889
921
|
});
|
|
@@ -931,7 +963,8 @@ async function importExternalTemplate(projectPath, projectName, template, logger
|
|
|
931
963
|
const { data, headers } = await external_axios_default().get(template, {
|
|
932
964
|
responseType: 'arraybuffer',
|
|
933
965
|
maxRedirects: 5,
|
|
934
|
-
timeout: NETWORK_TIMEOUT_MS
|
|
966
|
+
timeout: NETWORK_TIMEOUT_MS,
|
|
967
|
+
beforeRedirect: refuseHttpRedirect
|
|
935
968
|
});
|
|
936
969
|
const contentType = String(headers?.['content-type'] || '');
|
|
937
970
|
const looksZip = /zip|octet-stream/i.test(contentType) || template.toLowerCase().endsWith('.zip');
|
|
@@ -974,7 +1007,9 @@ async function importExternalTemplate(projectPath, projectName, template, logger
|
|
|
974
1007
|
return fallback;
|
|
975
1008
|
}
|
|
976
1009
|
}
|
|
977
|
-
|
|
1010
|
+
const insecureUrl = findInsecureTemplateUrlError(error);
|
|
1011
|
+
if (insecureUrl) logger.error(templateUrlNotHttps(insecureUrl.url));
|
|
1012
|
+
else if (error instanceof TemplateNotFoundError) logger.error(templateNotFoundInCatalog(templateName, error.cause));
|
|
978
1013
|
else if (error instanceof TemplateDownloadError) logger.error(templateDownloadFailed(templateName, error));
|
|
979
1014
|
else logger.error(installingFromTemplateError(templateName, error));
|
|
980
1015
|
await cleanupFailedImport(projectPath, ownsProjectDir, preExistingEntries);
|
|
@@ -14,6 +14,15 @@ export declare class TemplateNotFoundError extends Error {
|
|
|
14
14
|
readonly templateName: string;
|
|
15
15
|
constructor(templateName: string, cause?: unknown);
|
|
16
16
|
}
|
|
17
|
+
export declare class InsecureTemplateUrlError extends Error {
|
|
18
|
+
readonly url: string;
|
|
19
|
+
constructor(url: string);
|
|
20
|
+
}
|
|
21
|
+
export declare function isRefusedHttpTemplateUrl(url: string): boolean;
|
|
22
|
+
export declare function refuseHttpRedirect(options: {
|
|
23
|
+
protocol?: string;
|
|
24
|
+
href?: string;
|
|
25
|
+
}): void;
|
|
17
26
|
export declare class TemplateDownloadError extends Error {
|
|
18
27
|
readonly templateName: string;
|
|
19
28
|
constructor(templateName: string, cause: unknown);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"createdWith": "extension-create@4.1.
|
|
2
|
+
"createdWith": "extension-create@4.1.18",
|
|
3
3
|
"template": "typescript",
|
|
4
4
|
"source": "https://codeload.github.com/extension-js/examples/zip/e552f6db6f2862093ba9d7026e0d376f002d95cc",
|
|
5
5
|
"ref": "e552f6db6f2862093ba9d7026e0d376f002d95cc"
|
|
@@ -9,7 +9,7 @@ Packaging your extension is local and free. Submitting the result to a
|
|
|
9
9
|
store is what [extension.dev](https://docs.extension.dev/publish/overview?utm_source=store-md)
|
|
10
10
|
does, and it sponsors Extension.js.
|
|
11
11
|
|
|
12
|
-
Last updated: 2026-09-
|
|
12
|
+
Last updated: 2026-09-15
|
|
13
13
|
|
|
14
14
|
## Listing
|
|
15
15
|
|