create-nextly-app 0.0.2-alpha.70 → 0.0.2-alpha.72

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.
@@ -8053,6 +8053,38 @@ var require_utils$3 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8053
8053
  return output.join("");
8054
8054
  }
8055
8055
  /**
8056
+ * Removes dot segments from the path of a relative reference (a path that does
8057
+ * not start with "/") without changing what it resolves to against any base.
8058
+ * Unlike `removeDotSegments`, leading ".." segments that cannot be collapsed
8059
+ * are kept, the result never becomes root-relative, and a result that would be
8060
+ * empty or start with an empty segment is prefixed with "." (an empty path
8061
+ * means "same document" and a leading "//" would introduce an authority).
8062
+ *
8063
+ * @param {string} path
8064
+ * @returns {string}
8065
+ */
8066
+ function removeDotSegmentsRelative(path) {
8067
+ if (path === "" || path[0] === "/") return removeDotSegments(path);
8068
+ const segments = path.split("/");
8069
+ const last = segments.length - 1;
8070
+ /** @type {string[]} */
8071
+ const output = [];
8072
+ let trailingDirectory = false;
8073
+ for (let i = 0; i <= last; i++) {
8074
+ const segment = segments[i];
8075
+ if (segment === "." || segment === "..") {
8076
+ if (segment === "..") {
8077
+ if (output.length !== 0 && output[output.length - 1] !== "..") output.pop();
8078
+ else output.push("..");
8079
+ }
8080
+ trailingDirectory = i === last;
8081
+ } else output.push(segment);
8082
+ }
8083
+ if (trailingDirectory && output[output.length - 1] !== "..") output.push("");
8084
+ if (output.length === 0 || output[0] === "" || output[0].includes(":")) output.unshift(".");
8085
+ return output.join("/");
8086
+ }
8087
+ /**
8056
8088
  * Re-escape RFC 3986 gen-delims that must not appear literally in the host.
8057
8089
  * After the URI regex parses, these characters cannot be literal in the host
8058
8090
  * field, so any that appear after decoding came from percent-encoding and
@@ -8356,6 +8388,7 @@ var require_utils$3 = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8356
8388
  encodeFragment,
8357
8389
  escapePreservingEscapes,
8358
8390
  removeDotSegments,
8391
+ removeDotSegmentsRelative,
8359
8392
  isIPv4,
8360
8393
  isUUID,
8361
8394
  normalizeIPv6,
@@ -8545,18 +8578,24 @@ var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8545
8578
  };
8546
8579
  }));
8547
8580
  var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8548
- const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils$3();
8581
+ const { normalizeIPv6, removeDotSegments, removeDotSegmentsRelative, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils$3();
8549
8582
  const { SCHEMES, getSchemeHandler } = require_schemes();
8550
8583
  const VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
8551
8584
  const MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
8585
+ const MALFORMED_HOST_ERROR = "URI host is malformed.";
8586
+ const FORBIDDEN_DOMAIN_HOST_CHARACTER = /[\0-\x20"#/:<>?@[\\\]^`{|}\x7F]/u;
8552
8587
  /**
8588
+ * RFC 3986 section 3.1 does not allow percent-encoding in a scheme, so the
8589
+ * scheme is validated as-is and never decoded. Decoding would let inert input
8590
+ * such as `%6Aavascript:` become a live `javascript:` scheme.
8591
+ *
8553
8592
  * @param {string} scheme
8554
8593
  * @returns {string}
8555
8594
  */
8556
- function decodeValidScheme(scheme) {
8557
- const decodedScheme = unescape(String(scheme));
8558
- if (!VALID_SCHEME.test(decodedScheme)) throw new TypeError(MALFORMED_SCHEME_ERROR);
8559
- return decodedScheme;
8595
+ function validateScheme(scheme) {
8596
+ const stringScheme = String(scheme);
8597
+ if (!VALID_SCHEME.test(stringScheme)) throw new TypeError(MALFORMED_SCHEME_ERROR);
8598
+ return stringScheme;
8560
8599
  }
8561
8600
  /**
8562
8601
  * @template {import('./types/index').URIComponent|string} T
@@ -8584,6 +8623,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8584
8623
  const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
8585
8624
  const resolvedHost = resolved.host;
8586
8625
  const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
8626
+ if (hasForbiddenDomainHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP)) throw new Error(MALFORMED_HOST_ERROR);
8587
8627
  canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
8588
8628
  const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !/\P{ASCII}/u.test(resolvedHost);
8589
8629
  if (resolved.error && !encodedASCIIHost) throw new Error(resolved.error);
@@ -8627,10 +8667,11 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8627
8667
  } else {
8628
8668
  if (relative.path[0] === "/") target.path = removeDotSegments(relative.path);
8629
8669
  else {
8630
- if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) target.path = "/" + relative.path;
8670
+ const baseHasAuthority = base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0;
8671
+ if (baseHasAuthority && !base.path) target.path = "/" + relative.path;
8631
8672
  else if (!base.path) target.path = relative.path;
8632
8673
  else target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path;
8633
- target.path = removeDotSegments(target.path);
8674
+ target.path = base.scheme === void 0 && !baseHasAuthority && target.path[0] !== "/" ? removeDotSegmentsRelative(target.path) : removeDotSegments(target.path);
8634
8675
  }
8635
8676
  target.query = relative.query;
8636
8677
  }
@@ -8678,17 +8719,18 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8678
8719
  };
8679
8720
  const options = Object.assign({}, opts);
8680
8721
  const uriTokens = [];
8681
- if (component.scheme) component.scheme = decodeValidScheme(component.scheme);
8722
+ if (component.scheme) component.scheme = validateScheme(component.scheme);
8682
8723
  const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
8683
8724
  if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
8684
8725
  const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
8685
- const pathNoScheme = !options.skipEscape && component.scheme === void 0 && !hasAuthority;
8726
+ const schemelessReference = (component.scheme === void 0 || options.reference === "suffix") && !hasAuthority;
8727
+ const pathNoScheme = !options.skipEscape && schemelessReference;
8686
8728
  if (component.path !== void 0) {
8687
8729
  if (!options.skipEscape) component.path = serializePathEncoding(component.path, pathNoScheme);
8688
8730
  else component.path = normalizePercentEncoding(component.path);
8689
8731
  }
8690
8732
  if (options.reference !== "suffix" && component.scheme) {
8691
- component.scheme = decodeValidScheme(component.scheme);
8733
+ component.scheme = validateScheme(component.scheme);
8692
8734
  uriTokens.push(component.scheme, ":");
8693
8735
  }
8694
8736
  const authority = recomposeAuthority(component);
@@ -8699,8 +8741,8 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8699
8741
  }
8700
8742
  if (component.path !== void 0) {
8701
8743
  let s = component.path;
8702
- if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) s = removeDotSegments(s);
8703
- if (pathNoScheme) s = serializePathEncoding(s, true);
8744
+ if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) s = schemelessReference && s[0] !== "/" ? removeDotSegmentsRelative(s) : removeDotSegments(s);
8745
+ if (schemelessReference) s = serializePathEncoding(s, true);
8704
8746
  if (authority === void 0 && s[0] === "/" && s[1] === "/") s = "/%2F" + s.slice(2);
8705
8747
  uriTokens.push(s);
8706
8748
  }
@@ -8764,15 +8806,50 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8764
8806
  * @returns {boolean} whether host conversion failed
8765
8807
  */
8766
8808
  function canonicalizeHost(parsed, options, schemeHandler, isIP) {
8767
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) try {
8768
- parsed.host = new URL("http://" + parsed.host).hostname;
8769
- } catch (e) {
8770
- parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
8809
+ if (hasForbiddenDomainHost(parsed, options, schemeHandler, isIP)) {
8810
+ parsed.error = parsed.error || MALFORMED_HOST_ERROR;
8771
8811
  return true;
8772
8812
  }
8813
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
8814
+ let url;
8815
+ try {
8816
+ url = new URL("http://" + parsed.host);
8817
+ } catch (e) {
8818
+ parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
8819
+ return true;
8820
+ }
8821
+ if (!isHostOnlyURL(url)) {
8822
+ parsed.error = parsed.error || MALFORMED_HOST_ERROR;
8823
+ return true;
8824
+ }
8825
+ parsed.host = url.hostname;
8826
+ }
8773
8827
  return false;
8774
8828
  }
8775
8829
  /**
8830
+ * Whether a domain host contains an ASCII character that RFC 3986 does not
8831
+ * allow in a reg-name (unreserved, sub-delims, or pct-encoded). Such a host is
8832
+ * rejected instead of being handed to the WHATWG URL parser, which would treat
8833
+ * "@" as a userinfo delimiter (silently dropping the data before it) and strip
8834
+ * TAB, LF, and CR. Non-ASCII characters remain allowed for IDNs.
8835
+ *
8836
+ * @param {import('./types/index').URIComponent} parsed
8837
+ * @param {import('./types/index').Options} options
8838
+ * @param {{ domainHost?: boolean }|undefined} schemeHandler
8839
+ * @param {boolean} isIP
8840
+ * @returns {boolean}
8841
+ */
8842
+ function hasForbiddenDomainHost(parsed, options, schemeHandler, isIP) {
8843
+ return Boolean(parsed.host && isIP === false && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && FORBIDDEN_DOMAIN_HOST_CHARACTER.test(parsed.host));
8844
+ }
8845
+ /**
8846
+ * @param {URL} url
8847
+ * @returns {boolean}
8848
+ */
8849
+ function isHostOnlyURL(url) {
8850
+ return url.username === "" && url.password === "" && url.port === "" && url.pathname === "/" && url.search === "" && url.hash === "" && url.hostname !== "";
8851
+ }
8852
+ /**
8776
8853
  * @param {string} uri
8777
8854
  * @param {import('./types/index').Options} [opts]
8778
8855
  * @returns {{ parsed: import('./types/index').URIComponent, malformedAuthorityOrPort: boolean, malformedPercentEncoding: boolean, malformedSchemeSpecific: boolean, malformedHost: boolean, malformedScheme: boolean }}
@@ -8829,8 +8906,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8829
8906
  parsed.query = matches[7];
8830
8907
  parsed.fragment = matches[8];
8831
8908
  if (parsed.scheme !== void 0) {
8832
- const decodedScheme = unescape(parsed.scheme);
8833
- if (VALID_SCHEME.test(decodedScheme)) parsed.scheme = decodedScheme.toLowerCase();
8909
+ if (VALID_SCHEME.test(parsed.scheme)) parsed.scheme = parsed.scheme.toLowerCase();
8834
8910
  else {
8835
8911
  parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
8836
8912
  malformedScheme = true;
@@ -8853,7 +8929,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8853
8929
  malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
8854
8930
  parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
8855
8931
  if (malformedIPLiteral) {
8856
- parsed.error = parsed.error || "URI host is malformed.";
8932
+ parsed.error = parsed.error || MALFORMED_HOST_ERROR;
8857
8933
  malformedAuthorityOrPort = true;
8858
8934
  }
8859
8935
  } else isIP = true;
@@ -8865,13 +8941,12 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
8865
8941
  if (options.reference && options.reference !== "suffix" && options.reference !== parsed.reference) parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
8866
8942
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
8867
8943
  if (!malformedIPLiteral) malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
8944
+ if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
8945
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
8946
+ if (!isIP) host = normalizePercentEncoding(host.toLowerCase());
8947
+ parsed.host = isIP || options.scheme !== "null" ? reescapeHostDelimiters(host, isIP) : host;
8948
+ }
8868
8949
  if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
8869
- if (uri.indexOf("%") !== -1) {
8870
- if (parsed.host !== void 0 && !malformedIPLiteral) {
8871
- const host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
8872
- parsed.host = reescapeHostDelimiters(host, isIP);
8873
- }
8874
- }
8875
8950
  if (parsed.path) parsed.path = normalizePathEncoding(parsed.path);
8876
8951
  if (parsed.query) parsed.query = normalizeQueryFragmentEncoding(parsed.query);
8877
8952
  if (parsed.fragment) parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
@@ -46917,17 +46992,22 @@ async function generateRoutes(cwd, projectInfo) {
46917
46992
  * lists and deletes media, every request checked against the signed-in
46918
46993
  * caller's media permissions (`requireAuth: true` in the template);
46919
46994
  * - the public media route, `api/media/...`, which serves reads only;
46995
+ * - the uploads route, `uploads/...`, which serves files stored on local disk
46996
+ * that `next start` has no static file for, those uploaded since it started;
46920
46997
  * - the health check.
46921
46998
  *
46922
46999
  * `segment` is the URL each route answers. A handler already answering it
46923
47000
  * means the app has its own route there, which is left alone. Next.js leaves
46924
47001
  * route groups `(name)` and slots `@name` out of the URL wherever they sit,
46925
47002
  * and never routes a private folder `_name`, so the folders that answer a URL
46926
- * are found by that rule rather than by name. `catchAll` marks the optional
47003
+ * are found by that rule rather than by name. `catchAll` marks the
46927
47004
  * catch-alls, which serve everything below their URL too: a child folder
46928
- * holding a handler is weighed by {@link childFinding}.
47005
+ * holding a handler is weighed by {@link childFinding}. The uploads route is
47006
+ * a required catch-all rather than an optional one, and is weighed the same
47007
+ * way: a handler at `/uploads` itself, or any route below it, is the app's
47008
+ * own and leaves it unwritten.
46929
47009
  *
46930
- * `role` says what Nextly's route does there, for a media route the app
47010
+ * `role` says what Nextly's route does there, for a media or uploads route the app
46931
47011
  * already has something in place of; the report then carries the route to
46932
47012
  * add by hand.
46933
47013
  */
@@ -46955,6 +47035,20 @@ const ROUTES = [
46955
47035
  }
46956
47036
  ];
46957
47037
  /**
47038
+ * The uploads route, written by {@link generateUploadsRoute} beside the media
47039
+ * routes. Apart from them because it is a different concern: it serves the
47040
+ * files local storage keeps, where they serve the media library's records.
47041
+ */
47042
+ const UPLOADS_ROUTE = {
47043
+ path: "uploads/[...path]/route.ts",
47044
+ segment: "uploads",
47045
+ catchAll: true,
47046
+ importsConfig: true,
47047
+ role: (file) => `Nextly's uploads route at ${file} serves files stored on local disk at /uploads, which a production server otherwise answers 404 for until it restarts`
47048
+ };
47049
+ /** Every route the existing-app path writes, read from the templates together. */
47050
+ const ALL_ROUTES = [...ROUTES, UPLOADS_ROUTE];
47051
+ /**
46958
47052
  * The import that brings in the Nextly config: the template spells it through
46959
47053
  * the `@nextly-config` alias or a path relative to its own layout, neither of
46960
47054
  * which an existing app is guaranteed to resolve.
@@ -47041,7 +47135,7 @@ var UnreadableFolder = class extends Error {
47041
47135
  */
47042
47136
  async function readMediaRouteTemplates(templatesDir = resolveTemplatePath()) {
47043
47137
  const texts = /* @__PURE__ */ new Map();
47044
- for (const route of ROUTES) {
47138
+ for (const route of ALL_ROUTES) {
47045
47139
  const relative = `base/src/app/${route.path}`;
47046
47140
  let text;
47047
47141
  try {
@@ -47071,7 +47165,20 @@ async function readMediaRouteTemplates(templatesDir = resolveTemplatePath()) {
47071
47165
  * is for such an app.
47072
47166
  */
47073
47167
  async function generateMediaRoutes(cwd, projectInfo, templates) {
47074
- const prepared = ROUTES.map((route) => prepareRoute(cwd, projectInfo, templates, route));
47168
+ return writeRoutes(cwd, projectInfo, templates, ROUTES);
47169
+ }
47170
+ /**
47171
+ * Write the uploads route into an existing app, by the same rules as
47172
+ * {@link generateMediaRoutes}: never where the app already answers
47173
+ * `/uploads`, never through a link, and under `pageExtensions` reported with
47174
+ * its content rather than written.
47175
+ */
47176
+ async function generateUploadsRoute(cwd, projectInfo, templates) {
47177
+ return writeRoutes(cwd, projectInfo, templates, [UPLOADS_ROUTE]);
47178
+ }
47179
+ /** Write each of `routes` where nothing answers its URL, reporting the rest. */
47180
+ async function writeRoutes(cwd, projectInfo, templates, routes) {
47181
+ const prepared = routes.map((route) => prepareRoute(cwd, projectInfo, templates, route));
47075
47182
  const pages = await appPageExtensions(cwd);
47076
47183
  if (pages.extensions !== "absent") {
47077
47184
  const { configName, extensions } = pages;
@@ -47875,6 +47982,7 @@ async function generateIntoExistingApp(cwd, projectType, projectInfo, database,
47875
47982
  await generateRoutes(cwd, projectInfo),
47876
47983
  await generateAdminPage(cwd, projectInfo),
47877
47984
  await generateMediaRoutes(cwd, projectInfo, templates),
47985
+ await generateUploadsRoute(cwd, projectInfo, templates),
47878
47986
  await generateInstrumentation(cwd, projectInfo),
47879
47987
  await generateTypesDirectory(cwd, projectInfo),
47880
47988
  nextConfigFiles(await patchNextConfig(cwd, database)),
@@ -52547,4 +52655,4 @@ async function createNextly(options = {}) {
52547
52655
  }
52548
52656
  export { getAvailableTemplateNames as a, init as c, __require as d, __toESM$1 as f, validateTemplateName as i, shutdown as l, resolveProjectArg as n, packageVersion as o, validateProjectName as r, capture as s, createNextly as t, __commonJSMin$1 as u };
52549
52657
 
52550
- //# sourceMappingURL=create-nextly-5wiGMVI6.mjs.map
52658
+ //# sourceMappingURL=create-nextly-CyI8XgVc.mjs.map