@o3co/create-auth-provider 0.5.0 → 0.5.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.
@@ -1,8 +1,49 @@
1
1
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
- import { join } from "node:path";
3
+ import { join, posix, win32 } from "node:path";
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5
5
  import { isValidDirName, isValidProjectName, main, scaffold } from "../index.mjs";
6
+ import { shouldCopyTemplateEntry } from "../internal/template-filter.mjs";
7
+ // Both POSIX and Windows separators are exercised explicitly so the suite
8
+ // validates the separator-relative segment logic regardless of the host
9
+ // platform. (`path.sep` is the production default; tests pass it explicitly.)
10
+ const platforms = [
11
+ {
12
+ name: "POSIX-style paths",
13
+ sep: posix.sep,
14
+ installRoot: "/Users/x/.npm/_npx/abc/node_modules/@o3co/create-auth-provider/templates/standalone",
15
+ localRoot: "/repo/templates/standalone",
16
+ },
17
+ {
18
+ name: "Windows-style paths",
19
+ sep: win32.sep,
20
+ installRoot: "C:\\Users\\x\\AppData\\Roaming\\npm-cache\\_npx\\abc\\node_modules\\@o3co\\create-auth-provider\\templates\\standalone",
21
+ localRoot: "C:\\repo\\templates\\standalone",
22
+ },
23
+ ];
24
+ describe.each(platforms)("shouldCopyTemplateEntry on $name", ({ sep, installRoot, localRoot }) => {
25
+ const joinSegments = (base, ...rest) => [base, ...rest].join(sep);
26
+ it("includes the template root itself", () => {
27
+ expect(shouldCopyTemplateEntry(installRoot, installRoot, sep)).toBe(true);
28
+ });
29
+ it("includes a file directly under the template root even when ancestor path contains 'node_modules'", () => {
30
+ // Regression for v0.5.0 npx install bug: when the package is installed
31
+ // at .../node_modules/@o3co/create-auth-provider/..., the previous filter
32
+ // checked every segment of the absolute source path and excluded
33
+ // everything, so cpSync copied no files.
34
+ expect(shouldCopyTemplateEntry(joinSegments(installRoot, "package.json"), installRoot, sep)).toBe(true);
35
+ });
36
+ it("includes nested template files even when ancestor path contains 'node_modules' or 'dist'", () => {
37
+ expect(shouldCopyTemplateEntry(joinSegments(installRoot, "src", "app.mts"), installRoot, sep)).toBe(true);
38
+ expect(shouldCopyTemplateEntry(joinSegments(installRoot, "config", "application.conf"), installRoot, sep)).toBe(true);
39
+ });
40
+ it("excludes node_modules subdirectories that live INSIDE the template root", () => {
41
+ expect(shouldCopyTemplateEntry(joinSegments(localRoot, "node_modules", "foo", "index.js"), localRoot, sep)).toBe(false);
42
+ });
43
+ it("excludes dist subdirectories that live INSIDE the template root", () => {
44
+ expect(shouldCopyTemplateEntry(joinSegments(localRoot, "dist", "index.mjs"), localRoot, sep)).toBe(false);
45
+ });
46
+ });
6
47
  describe("scaffold", () => {
7
48
  let tempDir;
8
49
  beforeEach(() => {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=published-package.test.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"published-package.test.d.mts","sourceRoot":"","sources":["../../src/__tests__/published-package.test.mts"],"names":[],"mappings":""}
@@ -0,0 +1,73 @@
1
+ /*
2
+ * Copyright 2026 1o1 Co. Ltd.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ */
10
+ import { execFileSync } from "node:child_process";
11
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { dirname, join, resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ const CREATE_APP_DIR = resolve(__dirname, "../..");
18
+ describe("published-package install context (e2e)", () => {
19
+ let workspace;
20
+ let tarballPath;
21
+ beforeAll(() => {
22
+ workspace = mkdtempSync(join(tmpdir(), "create-auth-provider-e2e-"));
23
+ // Isolate npm's cache to a per-run directory so a polluted developer
24
+ // `~/.npm/_cacache/` (e.g. root-owned files left by a sudo install) cannot
25
+ // fail the test with EPERM. The prepack hook only needs to read the
26
+ // workspace, but `npm pack` itself touches the cache regardless.
27
+ const npmCache = join(workspace, "npm-cache");
28
+ // `npm pack` runs the `prepack` script (rimraf dist + copy-templates + tsc),
29
+ // so this exercises the same artifact npm publishes to the registry.
30
+ const stdout = execFileSync("npm", ["pack", "--pack-destination", workspace, "--json"], {
31
+ cwd: CREATE_APP_DIR,
32
+ encoding: "utf-8",
33
+ env: { ...process.env, npm_config_cache: npmCache },
34
+ });
35
+ const [packed] = JSON.parse(stdout);
36
+ tarballPath = join(workspace, packed.filename);
37
+ }, 120_000);
38
+ afterAll(() => {
39
+ if (workspace)
40
+ rmSync(workspace, { recursive: true, force: true });
41
+ });
42
+ it("scaffolds successfully when installed under a path containing 'node_modules'", () => {
43
+ // Regression for v0.5.0 npx bug: simulate the actual install layout
44
+ // (~/.npm/_npx/<hash>/node_modules/@o3co/create-auth-provider/) so the
45
+ // cpSync filter sees an absolute source path whose ancestors include
46
+ // 'node_modules'. Before the fix, the filter excluded every file and
47
+ // `cpSync` left the target directory empty.
48
+ const installRoot = join(workspace, "node_modules", "@o3co", "create-auth-provider");
49
+ mkdirSync(installRoot, { recursive: true });
50
+ execFileSync("tar", ["-xzf", tarballPath, "--strip-components=1", "-C", installRoot]);
51
+ const projectCwd = join(workspace, "scaffold-cwd");
52
+ mkdirSync(projectCwd);
53
+ execFileSync("node", [join(installRoot, "dist", "cli.mjs"), "my-test-project"], {
54
+ cwd: projectCwd,
55
+ encoding: "utf-8",
56
+ });
57
+ const targetDir = join(projectCwd, "my-test-project");
58
+ expect(existsSync(targetDir)).toBe(true);
59
+ expect(existsSync(join(targetDir, "package.json"))).toBe(true);
60
+ expect(existsSync(join(targetDir, "src", "app.mts"))).toBe(true);
61
+ expect(existsSync(join(targetDir, "config", "application.conf"))).toBe(true);
62
+ const pkg = JSON.parse(readFileSync(join(targetDir, "package.json"), "utf-8"));
63
+ expect(pkg.name).toBe("my-test-project");
64
+ for (const section of ["dependencies", "devDependencies", "peerDependencies"]) {
65
+ const deps = pkg[section];
66
+ if (!deps)
67
+ continue;
68
+ for (const [, version] of Object.entries(deps)) {
69
+ expect(version).not.toBe("workspace:*");
70
+ }
71
+ }
72
+ }, 30_000);
73
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAkCA,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OAIjD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAI7C,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,WAAW,MAAM,EAAE,aAAa,MAAM,KAAG,IA0CjE,CAAC;AAsDF,eAAO,MAAM,IAAI,QAAO,IA+CvB,CAAC"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OAIjD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAI7C,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,WAAW,MAAM,EAAE,aAAa,MAAM,KAAG,IAsCjE,CAAC;AAsDF,eAAO,MAAM,IAAI,QAAO,IA+CvB,CAAC"}
package/dist/index.mjs CHANGED
@@ -14,8 +14,9 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
17
- import { dirname, resolve, sep } from "node:path";
17
+ import { dirname, resolve } from "node:path";
18
18
  import { fileURLToPath } from "node:url";
19
+ import { shouldCopyTemplateEntry } from "./internal/template-filter.mjs";
19
20
  const __dirname = dirname(fileURLToPath(import.meta.url));
20
21
  const TEMPLATES_DIR = resolve(__dirname, "../templates/standalone");
21
22
  const getPackageVersions = () => {
@@ -47,13 +48,9 @@ export const scaffold = (targetDir, projectName) => {
47
48
  throw new Error(`Template directory not found at ${TEMPLATES_DIR}. If developing locally, run the prebuild script first.`);
48
49
  }
49
50
  // Copy template to target
50
- const EXCLUDED_DIRS = new Set(["node_modules", "dist"]);
51
51
  cpSync(TEMPLATES_DIR, targetDir, {
52
52
  recursive: true,
53
- filter: (source) => {
54
- const segments = source.split(sep);
55
- return !segments.some((s) => EXCLUDED_DIRS.has(s));
56
- },
53
+ filter: (source) => shouldCopyTemplateEntry(source, TEMPLATES_DIR),
57
54
  });
58
55
  // Rewrite package.json
59
56
  const pkgPath = resolve(targetDir, "package.json");
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Decide whether `cpSync` should copy a given source path.
3
+ *
4
+ * Only segments INSIDE `templateRoot` are checked against EXCLUDED_DIRS — the
5
+ * install-prefix path above the root (e.g. `~/.npm/_npx/<hash>/node_modules/...`
6
+ * when the package is run via `npx`) is ignored. Otherwise the filter would
7
+ * reject every file whenever the package itself happens to live under a
8
+ * `node_modules` directory (which is the v0.5.0 npx regression this fixes).
9
+ *
10
+ * The `pathSep` parameter exists so unit tests can exercise both POSIX and
11
+ * Windows separators regardless of the host platform; production callers omit
12
+ * it and pick up `path.sep` of the running platform. DO NOT change this to a
13
+ * hardcoded `"/"` — `cpSync` passes back-slash-delimited absolute paths on
14
+ * Windows, so segment splitting must follow the platform separator.
15
+ */
16
+ export declare const shouldCopyTemplateEntry: (source: string, templateRoot: string, pathSep?: string) => boolean;
17
+ //# sourceMappingURL=template-filter.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-filter.d.mts","sourceRoot":"","sources":["../../src/internal/template-filter.mts"],"names":[],"mappings":"AAuBA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,GACnC,QAAQ,MAAM,EACd,cAAc,MAAM,EACpB,UAAS,MAAY,KACnB,OAMF,CAAC"}
@@ -0,0 +1,45 @@
1
+ /*
2
+ * Copyright 2026 1o1 Co. Ltd.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ //
17
+ // INTERNAL — not exposed via the package's `exports` map. Reachable only from
18
+ // other files in this package (and its tests). See package.json `exports`.
19
+ //
20
+ import { sep } from "node:path";
21
+ const EXCLUDED_DIRS = new Set(["node_modules", "dist"]);
22
+ /**
23
+ * Decide whether `cpSync` should copy a given source path.
24
+ *
25
+ * Only segments INSIDE `templateRoot` are checked against EXCLUDED_DIRS — the
26
+ * install-prefix path above the root (e.g. `~/.npm/_npx/<hash>/node_modules/...`
27
+ * when the package is run via `npx`) is ignored. Otherwise the filter would
28
+ * reject every file whenever the package itself happens to live under a
29
+ * `node_modules` directory (which is the v0.5.0 npx regression this fixes).
30
+ *
31
+ * The `pathSep` parameter exists so unit tests can exercise both POSIX and
32
+ * Windows separators regardless of the host platform; production callers omit
33
+ * it and pick up `path.sep` of the running platform. DO NOT change this to a
34
+ * hardcoded `"/"` — `cpSync` passes back-slash-delimited absolute paths on
35
+ * Windows, so segment splitting must follow the platform separator.
36
+ */
37
+ export const shouldCopyTemplateEntry = (source, templateRoot, pathSep = sep) => {
38
+ if (source === templateRoot)
39
+ return true;
40
+ const prefix = templateRoot.endsWith(pathSep) ? templateRoot : `${templateRoot}${pathSep}`;
41
+ if (!source.startsWith(prefix))
42
+ return true;
43
+ const rel = source.slice(prefix.length);
44
+ return !rel.split(pathSep).some((s) => EXCLUDED_DIRS.has(s));
45
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o3co/create-auth-provider",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Scaffold a new o3co/auth.provider project from the standalone template",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@o3co/auth-provider-standalone",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "private": true,
5
5
  "license": "Apache-2.0",
6
6
  "packageManager": "pnpm@10.29.3",
@@ -1,10 +1,10 @@
1
1
  {
2
- "@o3co/auth-provider-core": "0.5.0",
3
- "@o3co/auth-provider-federation-google": "0.5.0",
4
- "@o3co/auth-provider-federation-github": "0.5.0",
5
- "@o3co/auth-provider-oauth": "0.5.0",
6
- "@o3co/auth-provider-oauth-token-exchange": "0.5.0",
7
- "@o3co/auth-provider-session": "0.5.0",
8
- "@o3co/auth-provider-foundation": "0.5.0",
9
- "@o3co/auth-provider-redis": "0.5.0"
2
+ "@o3co/auth-provider-core": "0.5.1",
3
+ "@o3co/auth-provider-federation-google": "0.5.1",
4
+ "@o3co/auth-provider-federation-github": "0.5.1",
5
+ "@o3co/auth-provider-oauth": "0.5.1",
6
+ "@o3co/auth-provider-oauth-token-exchange": "0.5.1",
7
+ "@o3co/auth-provider-session": "0.5.1",
8
+ "@o3co/auth-provider-foundation": "0.5.1",
9
+ "@o3co/auth-provider-redis": "0.5.1"
10
10
  }