@o3co/create-auth-provider 0.15.0 → 0.17.0-rc.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.
Files changed (92) hide show
  1. package/README.ja.md +139 -48
  2. package/README.md +138 -47
  3. package/dist/index.d.mts +33 -10
  4. package/dist/index.d.mts.map +1 -1
  5. package/dist/index.mjs +118 -59
  6. package/dist/internal/template-filter.d.mts +7 -10
  7. package/dist/internal/template-filter.d.mts.map +1 -1
  8. package/dist/internal/template-filter.mjs +7 -10
  9. package/package.json +6 -4
  10. package/templates/standalone/.dockerignore +15 -2
  11. package/templates/standalone/.env.example +69 -24
  12. package/templates/standalone/Dockerfile +7 -3
  13. package/templates/standalone/README.ja.md +677 -184
  14. package/templates/standalone/README.md +699 -238
  15. package/templates/standalone/config/application.conf +203 -325
  16. package/templates/standalone/config/clients.yaml +2 -0
  17. package/templates/standalone/config/clients.yaml.example +4 -0
  18. package/templates/standalone/config/development.conf +13 -4
  19. package/templates/standalone/config/mailpit.conf +8 -0
  20. package/templates/standalone/config/production.conf +5 -6
  21. package/templates/standalone/config/reference.conf +399 -0
  22. package/templates/standalone/docker-compose.mailpit.yml +36 -0
  23. package/templates/standalone/docker-compose.production.yml +58 -30
  24. package/templates/standalone/docker-compose.yml +13 -3
  25. package/templates/standalone/gitignore +4 -2
  26. package/templates/standalone/package.json +9 -6
  27. package/templates/standalone/src/__tests__/access-token-lifetime-alias.test.mts +65 -32
  28. package/templates/standalone/src/__tests__/adapters.test.mts +358 -0
  29. package/templates/standalone/src/__tests__/all-modules-composition.fixture.mts +1178 -0
  30. package/templates/standalone/src/__tests__/all-modules-composition.multi.test.mts +256 -0
  31. package/templates/standalone/src/__tests__/all-modules-composition.test.mts +1119 -0
  32. package/templates/standalone/src/__tests__/audit-sink.test.mts +78 -76
  33. package/templates/standalone/src/__tests__/configPath.test.mts +13 -11
  34. package/templates/standalone/src/__tests__/core-federations.test.mts +226 -0
  35. package/templates/standalone/src/__tests__/core-sections.test.mts +190 -0
  36. package/templates/standalone/src/__tests__/device-code-store-client-module.test.mts +9 -14
  37. package/templates/standalone/src/__tests__/documented-env-overrides.test.mts +672 -271
  38. package/templates/standalone/src/__tests__/federation-grants-composition.test.mts +480 -78
  39. package/templates/standalone/src/__tests__/federation-grants-survive-logout.test.mts +102 -66
  40. package/templates/standalone/src/__tests__/federation-token-refresh-outage.test.mts +135 -0
  41. package/templates/standalone/src/__tests__/google-federation-config-module.test.mts +49 -18
  42. package/templates/standalone/src/__tests__/google-session.fixture.mts +315 -0
  43. package/templates/standalone/src/__tests__/host-routes.test.mts +134 -0
  44. package/templates/standalone/src/__tests__/library-references.fixture.mts +183 -0
  45. package/templates/standalone/src/__tests__/listen.test.mts +121 -0
  46. package/templates/standalone/src/__tests__/logger-projection.test.mts +77 -0
  47. package/templates/standalone/src/__tests__/mail-sender.test.mts +134 -0
  48. package/templates/standalone/src/__tests__/mailpit-compose.test.mts +328 -0
  49. package/templates/standalone/src/__tests__/metrics.test.mts +17 -7
  50. package/templates/standalone/src/__tests__/mfa-switch.redis.test.mts +123 -0
  51. package/templates/standalone/src/__tests__/mfa-switch.test.mts +780 -0
  52. package/templates/standalone/src/__tests__/oidc-federation-config-module.test.mts +52 -46
  53. package/templates/standalone/src/__tests__/own-modules.test.mts +1457 -0
  54. package/templates/standalone/src/__tests__/post-logout-redirect.test.mts +183 -0
  55. package/templates/standalone/src/__tests__/redis-connection-error-log.test.mts +135 -0
  56. package/templates/standalone/src/__tests__/redis-rate-limiter-config-survives.test.mts +112 -81
  57. package/templates/standalone/src/__tests__/refresh-token-family-client-module.test.mts +25 -31
  58. package/templates/standalone/src/__tests__/replica-safety.test.mts +166 -145
  59. package/templates/standalone/src/__tests__/router-error-answers.test.mts +146 -0
  60. package/templates/standalone/src/__tests__/scaffold-assets.test.mts +275 -95
  61. package/templates/standalone/src/__tests__/sections.test.mts +480 -0
  62. package/templates/standalone/src/__tests__/session-logout-invalidates-token.test.mts +67 -55
  63. package/templates/standalone/src/__tests__/session-requirements.test.mts +299 -0
  64. package/templates/standalone/src/__tests__/shutdown.test.mts +319 -115
  65. package/templates/standalone/src/__tests__/smoke.test.mts +179 -233
  66. package/templates/standalone/src/__tests__/store-credential.test.mts +317 -0
  67. package/templates/standalone/src/__tests__/supertest-loopback.test.mts +182 -11
  68. package/templates/standalone/src/__tests__/three-tier-resolution.test.mts +108 -95
  69. package/templates/standalone/src/__tests__/two-phase-config.test.mts +438 -0
  70. package/templates/standalone/src/__tests__/upstream-amr.acceptance.test.mts +390 -0
  71. package/templates/standalone/src/adapters.mts +149 -0
  72. package/templates/standalone/src/app.mts +71 -115
  73. package/templates/standalone/src/buildModules.mts +229 -251
  74. package/templates/standalone/src/configPath.mts +422 -29
  75. package/templates/standalone/src/listen.mts +60 -0
  76. package/templates/standalone/src/logger.mts +31 -57
  77. package/templates/standalone/src/metrics.mts +24 -47
  78. package/templates/standalone/src/mfaSwitch.mts +262 -0
  79. package/templates/standalone/src/modules.mts +480 -464
  80. package/templates/standalone/src/rootRenames.mts +79 -0
  81. package/templates/standalone/src/routes.mts +89 -0
  82. package/templates/standalone/src/sections.mts +366 -0
  83. package/templates/standalone/src/shutdown.mts +54 -126
  84. package/templates/standalone/tests/index.test.js +2 -2
  85. package/templates/standalone/tests/package-lock.json +1615 -2223
  86. package/templates/standalone/tests/package.json +2 -2
  87. package/templates/standalone/tests/vitest.config.mjs +26 -0
  88. package/templates/standalone/vitest.config.mts +8 -7
  89. package/templates/standalone/vitest.supertest-loopback.mts +97 -69
  90. package/templates/versions.json +18 -15
  91. package/templates/standalone/src/__tests__/terminal-error.test.mts +0 -96
  92. package/templates/standalone/src/terminalError.mts +0 -60
package/README.ja.md CHANGED
@@ -1,11 +1,49 @@
1
1
  # @o3co/create-auth-provider
2
2
 
3
- auth.provider 用の CLI スキャフォルダーです。standalone テンプレートから新しいプロジェクトを生成します。
3
+ 最終更新: 2026-10-03
4
+
5
+ auth.provider 用の CLI スキャフォルダーです。内包するテンプレートの 1 つから新しいサーバープロジェクトを生成します。
6
+
7
+ ## 責務と役割
8
+
9
+ **役割。** リポジトリ内の [`templates/`](../templates) にあるテンプレート
10
+ (それぞれがデプロイ可能なコンポジションルート)の 1 つを、独立した新しい
11
+ プロジェクトに変換する `npx` の入口です。運用者のマシン上で一度だけ実行されます。生成された
12
+ プロジェクトはこのパッケージを import せず、このパッケージも `packages/*` の
13
+ ライブラリを一切 import しません。
14
+
15
+ **担うもの。** プロジェクト名・ディレクトリ名・テンプレート名の検証、選んだテンプレートのコピー、生成された
16
+ `package.json` の書き換え(名前、`workspace:*` → 公開バージョン)、プロジェクトの
17
+ `pnpm-workspace.yaml` の書き出し、そして一度きりの `pnpm-lock.yaml` の解決。
18
+
19
+ **担わないもの。** 生成されるプロジェクトの中身 — ソース、設定、Dockerfile、
20
+ テスト — はテンプレートのものです(このパッケージではなく `templates/<name>` を
21
+ 編集してください)。どのテンプレートがあり、それぞれ何のためにあるかは
22
+ [コンポジションテンプレートの ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md)
23
+ のものです。実行時の振る舞いは `@o3co/auth-provider-core` と、テンプレートが
24
+ 依存するライブラリのものです。
25
+
26
+ **別パッケージである理由。** `bin` 付きで単独公開されるため、provider をインストール
27
+ せずに `npx` で実行できます。公開 tarball にはモノレポが含まれないので、このパッケージは
28
+ すべてのテンプレートのコピーと、それらが固定するライブラリのバージョンを自前で持ちます
29
+ ([テンプレートの同梱方法](#テンプレートの同梱方法))。
4
30
 
5
31
  ## 使い方
6
32
 
7
33
  ```bash
8
- npx @o3co/create-auth-provider <project-name> [--dir <dir-name>]
34
+ npx @o3co/create-auth-provider <project-name> [--template <name>] [--dir <dir-name>] [--no-lockfile] [--no-mfa]
35
+ ```
36
+
37
+ `--template` はコピーするテンプレートを指定します(デフォルトは `standalone`)。
38
+ テンプレートは [`templates/`](../templates) 配下で `package.json` を持つディレクトリ
39
+ (シンボリックリンクでも、ドットで始まる名前でもないもの)で、それ以外の名前は、
40
+ 使えるテンプレートの一覧を示して拒否されます。各テンプレートが
41
+ 何のためにあるかはそれぞれの README に、テンプレートの分け方 — 機能ごとではなく
42
+ コンポジションの形ごとに 1 つ — は
43
+ [コンポジションテンプレートの ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md) にあります。
44
+
45
+ ```bash
46
+ npx @o3co/create-auth-provider my-auth-server --template standalone
9
47
  ```
10
48
 
11
49
  `<project-name>` はスコープ付き npm 名 (`@scope/pkg`) とスコープなしの名前 (`pkg`) のどちらでも指定できます。
@@ -15,8 +53,7 @@ npx @o3co/create-auth-provider <project-name> [--dir <dir-name>]
15
53
  ```bash
16
54
  npx @o3co/create-auth-provider my-auth-server
17
55
  cd my-auth-server
18
- npm install
19
- npm run debug
56
+ pnpm install
20
57
  ```
21
58
 
22
59
  スコープ付きの例(ディレクトリ名はパッケージ部分 `auth.provider` がデフォルト):
@@ -24,8 +61,7 @@ npm run debug
24
61
  ```bash
25
62
  npx @o3co/create-auth-provider @my-org/auth.provider
26
63
  cd auth.provider
27
- npm install
28
- npm run debug
64
+ pnpm install
29
65
  ```
30
66
 
31
67
  `--dir` でディレクトリ名を明示指定:
@@ -35,18 +71,84 @@ npx @o3co/create-auth-provider @my-org/auth.provider --dir provider
35
71
  cd provider
36
72
  ```
37
73
 
74
+ `--no-lockfile` を付けると lockfile の生成(下記の手順 7)を省略します。
75
+
76
+ スキャフォールドは、すべてのパスワードログインに第二要素を求めます: テンプレートの
77
+ MFA スイッチ `mfaMode`(`MFA_MODE`)のデフォルトは `required` です。development では
78
+ ほかに何も要りません。それ以外では、MFA に要るもの(2 つのストアを Redis に、鍵、
79
+ SMTP リレー)を設定するか、`MFA_MODE=off` で MFA を無効にするまで、
80
+ プロジェクトの起動は拒否されます。MFA ページのデフォルトは `/mfa` で、プロジェクトが
81
+ そこでページを用意しなければ、すべてのパスワードログインはそこで止まります。
82
+ スイッチが何をインストールし、何を要するかはテンプレートの README にあります。
83
+
84
+ `--no-mfa` を付けると、代わりにスイッチをスキャフォールド上で off と書きます。
85
+ `config/application.conf` の末尾に `mfaMode = "off"` と
86
+ `mfaMode = ${?MFA_MODE}` をこの順に追記するので、後のテンプレートがデフォルトを
87
+ 変えてもプロジェクトは off のままで、`MFA_MODE` を設定すれば作り直さずに MFA を
88
+ 有効にできます。MFA パッケージは依存に残り、ほかには何も削除・変更しません。
89
+
90
+ 生成されるプロジェクトは pnpm のプロジェクトです。`Dockerfile` は
91
+ `pnpm install --frozen-lockfile` でインストールし、ビルドの許可リストは
92
+ `pnpm-workspace.yaml` にあります。
93
+
94
+ CLI は最後のメッセージで次に `pnpm run debug` を実行するよう案内します。
95
+ スキャフォールドのデフォルトのままでは、これは起動しません: `.env` ファイルは
96
+ 読まず(読むのは compose ファイルだけ)、シェルの環境変数から issuer
97
+ (`OAUTH_JWT_ISSUER`)、署名鍵のペア、セッションシークレット、ユーザーサービスの
98
+ 2 つの URL(`REPOSITORIES_USER_HTTP_AUTHENTICATE_URL`、`REPOSITORIES_USER_HTTP_AUTHENTICATE_BY_TOKEN_URL`)、
99
+ そして `localhost:6379` の Redis を必要とします。コマンドは、プロジェクトに含まれる
100
+ テンプレートの README の[使い方](../templates/standalone/README.ja.md#使い方)にあります。
101
+
38
102
  ## 動作内容
39
103
 
40
- 1. `<project-name>` を検証する(バリデーションルール参照)。
104
+ 1. `<project-name>` を検証する([バリデーションルール](#バリデーションルール) 参照)。
41
105
  2. 生成先ディレクトリ名を決定する: `--dir <value>` が指定されていればその値、そうでなければスコープ付き名のパッケージ部分、最終的には入力値そのもの。
42
- 3. 生成先ディレクトリを `<cwd>/<dir-name>` として解決する。
43
- 4. ディレクトリがすでに存在する場合はエラーを出力して終了する。
44
- 5. `templates/standalone/` を生成先ディレクトリにコピーする(`node_modules/` と `dist/` は除外)。
45
- 6. 生成されたディレクトリの `package.json` を書き換える:
46
- - `name` を `<project-name>` をそのまま設定する(スコープを保持)。
47
- - `private` フィールドを削除する。
48
- - すべての `workspace:*` バージョン参照を `versions.json` の公開 semver バージョンに置き換える。
49
- 7. 次のステップを出力する。
106
+ 3. `--template`(デフォルト `standalone`)が内包するテンプレートの名前であることを確かめ、生成先ディレクトリを `<cwd>/<dir-name>` として解決し、すでに存在する場合はエラーで終了する。
107
+ 4. 指定したテンプレートを生成先ディレクトリにコピーし(`node_modules/` と `dist/` は除外)、`.gitignore` を復元する(npm は公開パッケージから `.gitignore` という名前のファイルを落とすため、tarball には `gitignore` として入っている)。`--no-mfa` のときは、その `config/application.conf` に MFA スイッチの行を追記する。
108
+ 5. 生成されたディレクトリの `package.json` を書き換える:
109
+ - `name` を `<project-name>` そのままに設定する(スコープを保持)。
110
+ - `"private": true` は意図的に残す: スキャフォールドされた ID プロバイダーが誤って公開できてはならないため。本当に公開するつもりなら自分でこのフィールドを削除する。
111
+ - `dependencies`・`devDependencies`・`peerDependencies` の各 `workspace:*` を、内包する `versions.json` の `^<version>` に置き換える。
112
+ 6. `bcrypt` 用の `onlyBuiltDependencies` 許可リストを書いた `pnpm-workspace.yaml` を生成する — pnpm 10.29 以降はこの許可リストを、単一パッケージのプロジェクトであってもこのファイルからしか読まない。
113
+ 7. `--no-lockfile` が指定されていなければ、依存関係を `pnpm-lock.yaml` に解決する(`pnpm install --lockfile-only --ignore-workspace`。`pnpm` が `PATH` にない場合は `corepack pnpm` 経由)。到達可能なレジストリが必要で、失敗しても警告を出すだけでスキャフォールドは完了する。
114
+ 8. 次のステップを出力する。
115
+
116
+ ### 生成される `pnpm-lock.yaml`
117
+
118
+ テンプレートの `Dockerfile` は `pnpm install --frozen-lockfile` でインストール
119
+ するため、生成されたプロジェクトは lockfile がなければそもそもビルドできません。
120
+ lockfile をテンプレートに同梱することはできません: 手順 5 がすべての
121
+ `workspace:*` を公開バージョンに置き換えるまで、lockfile が固定すべき依存関係の
122
+ 集合が存在しないからです。そこで、書き換え後の `package.json` に対してここで
123
+ 一度だけ解決します。**コミットしてください** — `docker build` を再現可能にするのは
124
+ この lockfile です。手順 7 が失敗した、または省略した場合は、プロジェクト内で一度
125
+ `pnpm install` を実行して結果をコミットしてください。
126
+
127
+ ## テンプレートの同梱方法
128
+
129
+ パッケージの `prebuild` と `prepack` スクリプトが
130
+ [`scripts/copy-templates.mjs`](scripts/copy-templates.mjs) を実行し、
131
+ すべてのテンプレートを `create-app/templates/<name>` にコピーし(git 管理外。
132
+ `node_modules/` と `dist/` は除外。コピー先は丸ごと作り直すので、リポジトリから
133
+ 消したテンプレートは残らない)、`create-app/templates/versions.json` —
134
+ 公開される `@o3co/auth-provider-*` 全パッケージの現在のバージョン — を書き出します。
135
+ tarball には両方が入り(`files: ["dist", "templates"]`)、`scaffold()` はそこから
136
+ 読みます。したがってスキャフォールドされるのは、このパッケージをビルドした時点の
137
+ テンプレートで、同じビルド時点のライブラリバージョンに固定されます。
138
+ テンプレートとは何か — `templates/` 配下のディレクトリで、シンボリックリンクでも
139
+ ドットで始まる名前でもなく、`package.json` を持ち、小文字のケバブケースで名付けられた
140
+ もの — は [`scripts/templates.mjs`](scripts/templates.mjs) の 1 か所で定義され、
141
+ コピーと CI の全テンプレートのビルドの両方がそれを使います。スキャフォルダーの
142
+ `availableTemplates()` は同じ規則でコピーを読みます。
143
+ [`published-package.test.mts`](src/__tests__/published-package.test.mts) は
144
+ パッケージを pack し、tarball がリポジトリのテンプレートをすべて含み、それぞれが
145
+ そこからスキャフォールドできることを確かめます。
146
+
147
+ CI は [`scripts/check-versions-json.mjs`](scripts/check-versions-json.mjs) を
148
+ 実行します。`packages/` 配下の公開パッケージが `copy-templates.mjs` のバージョン
149
+ 一覧に欠けているとき、または一覧がもう存在しないパッケージを挙げているときに
150
+ 失敗します — そうでなければスキャフォールドがそのパッケージの `workspace:*` の
151
+ バージョンを解決できずに失敗するためです。
50
152
 
51
153
  ## バリデーションルール
52
154
 
@@ -59,49 +161,38 @@ cd provider
59
161
 
60
162
  `--dir <value>` はスコープなしのパターンと同じ制約です。
61
163
 
164
+ `--template <name>` は内包するテンプレートの名前と完全に一致する必要があります。パスは
165
+ テンプレート名になりません。テンプレート名は小文字のケバブケース
166
+ `^[a-z0-9]+(?:-[a-z0-9]+)*$`(英小文字と数字の並びを 1 つのハイフンでつなぐ)で、
167
+ それ以外の名前のテンプレートはビルドが拒否します。
168
+
62
169
  ## 既知の制約
63
170
 
64
- 内包されているテンプレートの `README.md` / `README.ja.md` の見出しは `@o3co/auth-provider-standalone` のままです。スコープ付きでプロジェクトを生成した場合、この見出しは生成された `package.json` の `name` と一致しません。必要に応じて手動で修正してください。
171
+ - 内包されている各テンプレートの `README.md` / `README.ja.md` の見出しは上流の名前(たとえば `@o3co/auth-provider-standalone`)のままです。スコープ付きでプロジェクトを生成した場合、この見出しは `package.json` の名前と一致しません。必要に応じて手動で修正してください。
172
+ - テンプレートの README は、このリポジトリ内を相対パス(`../../docs/…`、`../../packages/…`)でリンクしています。これはモノレポ内では解決しますが、横に `docs/` も `packages/` もないスキャフォールド先のプロジェクトでは解決しません。GitHub 上で参照してください。
65
173
 
66
174
  ## 生成される構造
67
175
 
68
- ```
69
- <プロジェクト名>/
70
- ├── config/
71
- │ ├── application.conf # HOCON 設定(環境変数で上書き可能)
72
- │ ├── clients.yaml # OAuth クライアントレジストリ
73
- │ └── clients.yaml.example
74
- ├── src/
75
- │ └── app.mts # Composition root
76
- ├── tests/
77
- ├── .dockerignore
78
- ├── .env.example
79
- ├── Dockerfile
80
- ├── docker-compose.yml
81
- ├── docker-compose.test.yml
82
- ├── Makefile
83
- ├── package.json
84
- ├── tsconfig.json
85
- └── vitest.config.mts
86
- ```
87
-
88
- 生成後、`.env.example` をコピーして `.env` を作成し、必須の環境変数を設定してください。
176
+ 生成されるプロジェクトは、選んだテンプレート(たとえば
177
+ [`templates/standalone`](../templates/standalone))の完全なコピー(`node_modules/` と `dist/` を除く)に、`pnpm-workspace.yaml`(手順 6)と、
178
+ `--no-lockfile` を指定せず lockfile の手順が失敗しなかった場合は `pnpm-lock.yaml`(手順 7)を加えたものです。どの
179
+ ファイルがモジュール構成で、どれがホストプロセスで、スキャフォールドが何を
180
+ 持つかは、テンプレートの README に記述されています。
89
181
 
90
182
  ## プログラマティック API
91
183
 
92
- ```typescript
93
- import { scaffold, main } from "@o3co/create-auth-provider";
94
-
95
- // 絶対パスにプロジェクトを生成する
96
- scaffold(targetDir: string, projectName: string): void;
97
-
98
- // CLI エントリーポイント — process.argv を読み取り、エラー時は終了する
99
- main(): void;
100
- ```
184
+ モジュールは CLI を構成する関数を export しています。シグネチャは
185
+ [`src/index.mts`](src/index.mts) にあります。
101
186
 
102
- テンプレートディレクトリが見つからない場合、または `workspace:*` 依存関係が `versions.json` で解決できない場合、`scaffold` はエラーをスローします。
187
+ - `scaffold(targetDir, projectName, template?, options?)` — 手順 4〜6。`template`(デフォルトは `DEFAULT_TEMPLATE` = `"standalone"`)からコピーする。`options.noMfa` は `--no-mfa` にあたる。コピーできないときは何も書かずに `templateRefusal` のメッセージで例外を投げる。`workspace:*` 依存が `versions.json` に見つからない場合も例外を投げる。
188
+ - `availableTemplates(templatesRoot?)` — 内包するテンプレートの名前(ソート済み)。
189
+ - `templateRefusal(template, templates)` — `template` からスキャフォールドできない理由(1 つも内包されていない、またはそのどれでもない)。できるときは `undefined`。
190
+ - `generateLockfile(targetDir)` — 手順 7。例外を投げず、`{ ok: true, command }` か `{ ok: false, reason }` を返す。
191
+ - `main()` — CLI 本体。`process.argv` を読み、不正な引数、未知のテンプレート、既存のディレクトリに対しては非ゼロで終了する。
192
+ - `isValidProjectName(name)` / `isValidDirName(name)` — [バリデーションルール](#バリデーションルール)。
103
193
 
104
194
  ## 関連
105
195
 
106
- - [`@o3co/auth-provider-standalone`](../templates/standalone) — このツールが生成元とするテンプレート
196
+ - [`templates/`](../templates) — このツールが生成元とするテンプレート。[`@o3co/auth-provider-standalone`](../templates/standalone) がデフォルト
197
+ - [コンポジションテンプレートの ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md) — テンプレートをこう分ける理由
107
198
  - [`@o3co/auth-provider-core`](../packages/core) — コアアプリケーションファクトリ
package/README.md CHANGED
@@ -1,11 +1,49 @@
1
1
  # @o3co/create-auth-provider
2
2
 
3
- CLI scaffolder for auth.provider. Generates a new project from the standalone template.
3
+ Last updated: 2026-10-03
4
+
5
+ CLI scaffolder for auth.provider. Generates a new server project from one of the built-in templates.
6
+
7
+ ## Responsibility
8
+
9
+ **Role.** The `npx` entry point that turns one of the in-repo templates under
10
+ [`templates/`](../templates) — each a deployable composition root — into a new,
11
+ independent project. It runs once, on the operator's machine; nothing in the generated
12
+ project imports it, and it imports none of the `packages/*` libraries.
13
+
14
+ **Owns.** Project-name, directory and template validation, copying the
15
+ chosen template, rewriting the generated `package.json` (name, `workspace:*`
16
+ → published versions), writing the project's `pnpm-workspace.yaml`, and the
17
+ one-time `pnpm-lock.yaml` resolution.
18
+
19
+ **Does not own.** The content of the generated project — source, config,
20
+ Dockerfile, tests — which is the template's (edit `templates/<name>`, not
21
+ this package); and which templates there are, and what each is for — the
22
+ [composition templates ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md).
23
+ Runtime behaviour belongs to `@o3co/auth-provider-core` and the libraries the
24
+ template depends on.
25
+
26
+ **Why a separate package.** It is published on its own with a `bin`, so it can
27
+ be run with `npx` without installing the provider. The monorepo is not in the
28
+ published tarball, so the package carries its own copy of every template and
29
+ of the library versions they pin ([How the templates are bundled](#how-the-templates-are-bundled)).
4
30
 
5
31
  ## Usage
6
32
 
7
33
  ```bash
8
- npx @o3co/create-auth-provider <project-name> [--dir <dir-name>]
34
+ npx @o3co/create-auth-provider <project-name> [--template <name>] [--dir <dir-name>] [--no-lockfile] [--no-mfa]
35
+ ```
36
+
37
+ `--template` names the template to copy, `standalone` by default. The
38
+ templates are the directories under [`templates/`](../templates) that hold a
39
+ `package.json` (not a symbolic link, not dot-named), and the CLI refuses any
40
+ other name, listing the ones it has.
41
+ What each template is for is in its own README; how the set is drawn — a
42
+ template per composition shape, not per feature — is in the
43
+ [composition templates ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md).
44
+
45
+ ```bash
46
+ npx @o3co/create-auth-provider my-auth-server --template standalone
9
47
  ```
10
48
 
11
49
  `<project-name>` may be either a scoped npm name (`@scope/pkg`) or an unscoped name (`pkg`).
@@ -15,8 +53,7 @@ Unscoped example:
15
53
  ```bash
16
54
  npx @o3co/create-auth-provider my-auth-server
17
55
  cd my-auth-server
18
- npm install
19
- npm run debug
56
+ pnpm install
20
57
  ```
21
58
 
22
59
  Scoped example (directory defaults to the package portion `auth.provider`):
@@ -24,8 +61,7 @@ Scoped example (directory defaults to the package portion `auth.provider`):
24
61
  ```bash
25
62
  npx @o3co/create-auth-provider @my-org/auth.provider
26
63
  cd auth.provider
27
- npm install
28
- npm run debug
64
+ pnpm install
29
65
  ```
30
66
 
31
67
  Override the directory name with `--dir`:
@@ -35,18 +71,84 @@ npx @o3co/create-auth-provider @my-org/auth.provider --dir provider
35
71
  cd provider
36
72
  ```
37
73
 
74
+ `--no-lockfile` skips the lockfile step (step 7 below).
75
+
76
+ The scaffold requires a second factor at every password login: the
77
+ template's MFA switch, `mfaMode` (`MFA_MODE`), defaults to `required`. In
78
+ development that needs nothing more; outside it, the project's boot is
79
+ refused until it sets what MFA needs (its two stores on Redis, a key, an SMTP
80
+ relay) or turns MFA off with `MFA_MODE=off`. The MFA page defaults to `/mfa`,
81
+ which the project must serve, or every password login stops there. What the
82
+ switch installs and needs is in the template's README.
83
+
84
+ `--no-mfa` writes the switch off in the scaffold instead:
85
+ it appends `mfaMode = "off"` and then `mfaMode = ${?MFA_MODE}` to
86
+ `config/application.conf`, so the project stays off whatever default a later
87
+ template ships, and `MFA_MODE` still turns MFA on without re-scaffolding. The
88
+ MFA package stays a dependency; nothing else is removed or changed.
89
+
90
+ The generated project is a pnpm project: its `Dockerfile` installs with
91
+ `pnpm install --frozen-lockfile`, and its build allowlist lives in
92
+ `pnpm-workspace.yaml`.
93
+
94
+ The CLI's closing message suggests `pnpm run debug` next. On the scaffold's
95
+ defaults alone that does not boot: it reads no `.env` file (only the compose
96
+ files do), and needs from the shell's environment an issuer
97
+ (`OAUTH_JWT_ISSUER`), a signing key pair, a session secret, the two URLs of
98
+ your user service (`REPOSITORIES_USER_HTTP_AUTHENTICATE_URL`,
99
+ `REPOSITORIES_USER_HTTP_AUTHENTICATE_BY_TOKEN_URL`) and a Redis on `localhost:6379`. The
100
+ template's README, which the project carries, gives the commands under
101
+ [Usage](../templates/standalone/README.md#usage).
102
+
38
103
  ## What It Does
39
104
 
40
- 1. Validates `<project-name>` (see Validation Rules).
105
+ 1. Validates `<project-name>` (see [Validation Rules](#validation-rules)).
41
106
  2. Derives the target directory name: `--dir <value>` if given, else the unscoped part of a scoped name, else the name itself.
42
- 3. Resolves the target directory as `<cwd>/<dir-name>`.
43
- 4. Errors if the directory already exists.
44
- 5. Copies `templates/standalone/` to the target directory, excluding `node_modules/` and `dist/`.
45
- 6. Rewrites `package.json` in the generated directory:
107
+ 3. Checks that `--template` (default `standalone`) names a bundled template, and resolves the target directory as `<cwd>/<dir-name>`, erroring if it already exists.
108
+ 4. Copies the named template to the target directory, excluding `node_modules/` and `dist/`, and restores its `.gitignore` (the tarball carries it as `gitignore`, because npm drops a file named `.gitignore` from a published package). With `--no-mfa`, appends the MFA switch lines to its `config/application.conf`.
109
+ 5. Rewrites `package.json` in the generated directory:
46
110
  - Sets `name` to `<project-name>` verbatim (scope-preserving).
47
- - Removes the `private` field.
48
- - Replaces all `workspace:*` version references with published semver versions from `versions.json`.
49
- 7. Prints next-step instructions.
111
+ - Keeps `"private": true` on purpose: a scaffolded identity provider should not be publishable by accident. Remove the field yourself if you really intend to publish.
112
+ - Replaces each `workspace:*` version in `dependencies`, `devDependencies` and `peerDependencies` with `^<version>` from the bundled `versions.json`.
113
+ 6. Writes `pnpm-workspace.yaml` with the `onlyBuiltDependencies` allowlist for `bcrypt` — pnpm 10.29 and later read that allowlist only from this file, in a single-package project too.
114
+ 7. Resolves the dependency set into `pnpm-lock.yaml` (`pnpm install --lockfile-only --ignore-workspace`, through `corepack pnpm` when `pnpm` is not on `PATH`), unless `--no-lockfile` was passed. This needs a reachable registry; a failure prints a warning and the scaffold still completes.
115
+ 8. Prints next-step instructions.
116
+
117
+ ### The generated `pnpm-lock.yaml`
118
+
119
+ The template's `Dockerfile` installs with `pnpm install --frozen-lockfile`, so
120
+ the generated project needs a lockfile to build at all. It cannot ship with the
121
+ template: until step 5 has replaced every `workspace:*` with a published
122
+ version, the dependency set the lockfile would have to pin does not exist. So
123
+ it is resolved once, here, against the rewritten `package.json`. **Commit it** —
124
+ it is what makes `docker build` reproducible. If step 7 failed or was skipped,
125
+ run `pnpm install` in the project once and commit the result.
126
+
127
+ ## How the templates are bundled
128
+
129
+ The package's `prebuild` and `prepack` scripts run
130
+ [`scripts/copy-templates.mjs`](scripts/copy-templates.mjs), which copies
131
+ every template into `create-app/templates/<name>` (git-ignored; `node_modules/`
132
+ and `dist/` excluded; the destination is rebuilt whole, so a template removed
133
+ from the repository does not linger) and writes `create-app/templates/versions.json`, the current version
134
+ of every published `@o3co/auth-provider-*` package. The tarball ships both
135
+ (`files: ["dist", "templates"]`), and `scaffold()` reads them from there. A
136
+ scaffold is therefore the template as it was when this package was built,
137
+ pinned to the library versions of that same build.
138
+ What a template is — a directory under `templates/`, not a symbolic link and
139
+ not dot-named, holding a `package.json`, named in lowercase kebab-case — is
140
+ defined once, in [`scripts/templates.mjs`](scripts/templates.mjs), which the
141
+ copy and CI's build of every template both use; the scaffolder's
142
+ `availableTemplates()` reads the copy by the same rule.
143
+ [`published-package.test.mts`](src/__tests__/published-package.test.mts)
144
+ packs the package and holds the tarball to the repository: it must ship every
145
+ template, and each must scaffold from it.
146
+
147
+ CI runs [`scripts/check-versions-json.mjs`](scripts/check-versions-json.mjs),
148
+ which fails when a published package under `packages/` is missing from
149
+ `copy-templates.mjs`'s version list, or the list names one that no longer
150
+ exists — a scaffold would otherwise fail to resolve that package's
151
+ `workspace:*` version.
50
152
 
51
153
  ## Validation Rules
52
154
 
@@ -59,49 +161,38 @@ Both forms must be non-empty, not `.` or `..`, and ≤ 214 characters.
59
161
 
60
162
  `--dir <value>` must match the unscoped pattern above (same constraints).
61
163
 
164
+ `--template <name>` must be the name of a bundled template, exactly; a path is
165
+ never one. A template's name is lowercase kebab-case,
166
+ `^[a-z0-9]+(?:-[a-z0-9]+)*$` (lowercase letters and digits, in parts joined by
167
+ single hyphens); the build refuses a template named otherwise.
168
+
62
169
  ## Known Limitations
63
170
 
64
- The bundled template's `README.md` / `README.ja.md` still carry the upstream title `@o3co/auth-provider-standalone`. When generating a scoped project, that title will not match your `package.json` name; edit it manually if it matters for your use case.
171
+ - Each bundled template's `README.md` / `README.ja.md` carry its upstream title, such as `@o3co/auth-provider-standalone`. When generating a scoped project, that title will not match your `package.json` name; edit it manually if it matters for your use case.
172
+ - The template's README links into this repository with relative paths (`../../docs/…`, `../../packages/…`). Those resolve in the monorepo and not in a scaffolded project, where there is no `docs/` or `packages/` beside it; read them on GitHub instead.
65
173
 
66
174
  ## Generated Structure
67
175
 
68
- ```
69
- <project-name>/
70
- ├── config/
71
- │ ├── application.conf # HOCON config (overridable via env vars)
72
- │ ├── clients.yaml # OAuth client registry
73
- │ └── clients.yaml.example
74
- ├── src/
75
- │ └── app.mts # Composition root
76
- ├── tests/
77
- ├── .dockerignore
78
- ├── .env.example
79
- ├── Dockerfile
80
- ├── docker-compose.yml
81
- ├── docker-compose.test.yml
82
- ├── Makefile
83
- ├── package.json
84
- ├── tsconfig.json
85
- └── vitest.config.mts
86
- ```
87
-
88
- After generation, fill in the required environment variables in `.env` (copy from `.env.example`).
176
+ The generated project is a complete copy of the chosen template, such as
177
+ [`templates/standalone`](../templates/standalone) (without `node_modules/` and
178
+ `dist/`) plus `pnpm-workspace.yaml` (step 6) and, unless `--no-lockfile` was
179
+ given or the lockfile step failed, `pnpm-lock.yaml` (step 7). The template's README describes its layout — which file is
180
+ the composition, which is the host process, and what the scaffold owns.
89
181
 
90
182
  ## Programmatic API
91
183
 
92
- ```typescript
93
- import { scaffold, main } from "@o3co/create-auth-provider";
94
-
95
- // Generate a project at an absolute path
96
- scaffold(targetDir: string, projectName: string): void;
97
-
98
- // CLI entry point — reads process.argv and exits on error
99
- main(): void;
100
- ```
184
+ The module exports the functions the CLI is built from; their signatures are
185
+ in [`src/index.mts`](src/index.mts).
101
186
 
102
- `scaffold` throws if the template directory is missing or if a `workspace:*` dependency cannot be resolved in `versions.json`.
187
+ - `scaffold(targetDir, projectName, template?, options?)` — steps 4–6, from `template` (default `DEFAULT_TEMPLATE`, `"standalone"`); `options.noMfa` is `--no-mfa`. Throws, before writing anything, with `templateRefusal`'s message; and throws if a `workspace:*` dependency has no entry in `versions.json`.
188
+ - `availableTemplates(templatesRoot?)` — the bundled templates' names, sorted.
189
+ - `templateRefusal(template, templates)` — why `template` cannot be scaffolded (none bundled, or not one of them), or `undefined`.
190
+ - `generateLockfile(targetDir)` — step 7. Returns `{ ok: true, command }` or `{ ok: false, reason }` rather than throwing.
191
+ - `main()` — the CLI: reads `process.argv`, and exits non-zero on an invalid argument, an unknown template or an existing directory.
192
+ - `isValidProjectName(name)` / `isValidDirName(name)` — the [Validation Rules](#validation-rules).
103
193
 
104
194
  ## See Also
105
195
 
106
- - [`@o3co/auth-provider-standalone`](../templates/standalone) — The template this tool generates from
196
+ - [`templates/`](../templates) — The templates this tool generates from; [`@o3co/auth-provider-standalone`](../templates/standalone) is the default
197
+ - [Composition templates ADR](../packages/core/docs/adr/2026-09-29-composition-templates.md) — Why the templates are split as they are
107
198
  - [`@o3co/auth-provider-core`](../packages/core) — Core application factory
package/dist/index.d.mts CHANGED
@@ -1,6 +1,32 @@
1
+ /** The template a scaffold is made from when none is named. */
2
+ export declare const DEFAULT_TEMPLATE = "standalone";
3
+ /**
4
+ * The templates under `templatesRoot`: every directory — not a symbolic link,
5
+ * not dot-named — holding a `package.json`, sorted; none when the directory is
6
+ * missing. This is the rule `scripts/templates.mjs` copies templates by, read
7
+ * here from the copy, where `versions.json` sits beside them. A name is only
8
+ * ever looked up in this list, so nothing outside the templates directory can
9
+ * be named as a template.
10
+ */
11
+ export declare const availableTemplates: (templatesRoot?: string) => string[];
12
+ /**
13
+ * Why `template` cannot be scaffolded from `templates`, or `undefined` when it
14
+ * can: none is bundled (a checkout that never ran the prebuild script), or it
15
+ * is not one of them.
16
+ */
17
+ export declare const templateRefusal: (template: string, templates: readonly string[]) => string | undefined;
1
18
  export declare const isValidProjectName: (name: string) => boolean;
2
19
  export declare const isValidDirName: (name: string) => boolean;
3
- export declare const scaffold: (targetDir: string, projectName: string) => void;
20
+ /** What `scaffold()` changes in the copy beyond the name and the versions. */
21
+ export interface ScaffoldOptions {
22
+ /**
23
+ * Write the template's MFA switch off (`MFA_OFF_LINES`), so the scaffold
24
+ * stays off whatever default a later template ships; `MFA_MODE` still
25
+ * turns MFA on.
26
+ */
27
+ readonly noMfa?: boolean;
28
+ }
29
+ export declare const scaffold: (targetDir: string, projectName: string, template?: string, options?: ScaffoldOptions) => void;
4
30
  /** Outcome of the scaffold-time lockfile generation. */
5
31
  export type LockfileResult = {
6
32
  readonly ok: true;
@@ -10,15 +36,12 @@ export type LockfileResult = {
10
36
  readonly reason: string;
11
37
  };
12
38
  /**
13
- * Resolve the scaffolded project's dependency graph into `pnpm-lock.yaml`.
14
- *
15
- * This is what makes the template's `pnpm install --frozen-lockfile` build
16
- * possible: the lockfile cannot be shipped with the template, because the
17
- * dependency set it would pin does not exist until `scaffold` has replaced
18
- * every `workspace:*` with a published version (#289). It is therefore
19
- * resolved once, here, against the rewritten `package.json`. Best-effort by
20
- * design — it needs a package manager and a reachable registry — so failure
21
- * is a result, not a throw.
39
+ * Resolve the scaffolded project's dependency graph into `pnpm-lock.yaml`,
40
+ * which the template's `pnpm install --frozen-lockfile` build needs. The
41
+ * lockfile cannot ship with the template: the dependency set it pins exists
42
+ * only after `scaffold` has replaced every `workspace:*`. Best-effort (it needs
43
+ * a package manager and a reachable registry), so failure is a result, not a
44
+ * throw.
22
45
  */
23
46
  export declare const generateLockfile: (targetDir: string) => LockfileResult;
24
47
  export declare const main: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAoCA,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,IAyEjE,CAAC;AAEF,wDAAwD;AACxD,MAAM,MAAM,cAAc,GACvB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAqBnD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,MAAM,KAAG,cA6BpD,CAAC;AA0DF,eAAO,MAAM,IAAI,QAAO,IAmEvB,CAAC"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAgCA,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAE7C;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,gBAAe,MAAuB,KAAG,MAAM,EAWjF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAC3B,UAAU,MAAM,EAChB,WAAW,SAAS,MAAM,EAAE,KAC1B,MAAM,GAAG,SAQX,CAAC;AAcF,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OAIjD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,OAI7C,CAAC;AAEF,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CACzB;AAeD,eAAO,MAAM,QAAQ,GACpB,WAAW,MAAM,EACjB,aAAa,MAAM,EACnB,WAAU,MAAyB,EACnC,UAAS,eAAoB,KAC3B,IAwEF,CAAC;AAEF,wDAAwD;AACxD,MAAM,MAAM,cAAc,GACvB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAkBnD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,MAAM,KAAG,cA6BpD,CAAC;AA4EF,eAAO,MAAM,IAAI,QAAO,IA6EvB,CAAC"}