create-sailor 1.4.3 → 1.5.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/index.js +177 -170
- package/package.json +1 -1
- package/templates/orm/drizzle/README.md +58 -0
- package/templates/orm/drizzle/drizzle.config.ts +16 -0
- package/templates/orm/drizzle/migrations/.gitkeep +0 -0
- package/templates/orm/drizzle/package.json +29 -0
- package/templates/orm/drizzle/src/index.ts +31 -0
- package/templates/orm/drizzle/src/schema/auth.ts +73 -0
- package/templates/orm/drizzle/src/schema/billing.ts +37 -0
- package/templates/orm/drizzle/src/schema/index.ts +3 -0
- package/templates/orm/drizzle/src/schema/tenant.ts +57 -0
- package/templates/orm/drizzle/tsconfig.json +16 -0
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{execSync as
|
|
3
|
-
`));return}
|
|
2
|
+
import{execSync as vt}from"child_process";import fe from"fs";import _t from"path";import*as x from"@clack/prompts";import{Command as va}from"commander";import C from"picocolors";import _a from"update-notifier";import Sr from"cfonts";import hr from"picocolors";import mn from"fs";import Gt from"path";import{fileURLToPath as fr}from"url";function yr(){try{let t=Gt.dirname(fr(import.meta.url));for(let o=0;o<4;o++){let n=Gt.join(t,"package.json");if(mn.existsSync(n)){let r=mn.readFileSync(n,"utf-8"),s=JSON.parse(r);if(s.name==="create-sailor"&&typeof s.version=="string")return s.version}t=Gt.dirname(t)}return"0.0.0-dev"}catch{return"0.0.0-dev"}}var ae=yr();function gn(){if(process.env.NO_COLOR||!process.stdout.isTTY){process.stdout.write([`Sailor v${ae}`,"AI-Native SaaS Unicorn Template","by Nebutra \xB7 https://nebutra.com","",""].join(`
|
|
3
|
+
`));return}Sr.say("Sailor",{font:"block",align:"left",colors:["#0033FE","#0BF1C3"],gradient:["#0033FE","#0BF1C3"],transitionGradient:!0,space:!1}),process.stdout.write(hr.dim(` AI-Native SaaS Template \xB7 v${ae} \xB7 nebutra.com
|
|
4
4
|
|
|
5
|
-
`))}import P from"picocolors";var
|
|
5
|
+
`))}import P from"picocolors";var Er={qstash:"foundation",bullmq:"foundation",upstash:"foundation",sqs:"foundation",meilisearch:"foundation",typesense:"foundation",algolia:"foundation",pgvector:"foundation",novu:"foundation",knock:"foundation",svix:"foundation","vercel-flags":"wip",growthbook:"wip",configcat:"wip",turnstile:"wip",hcaptcha:"wip","aliyun-slide":"wip"};function vr(e){return e?Er[e]??"stable":"stable"}function Qe(e){switch(e){case"stable":return"";case"foundation":return"[Foundation]";case"wip":return"[WIP]";case"deprecated":return"[Deprecated]"}}function ce(e){switch(e){case"stable":return"Production-ready.";case"foundation":return"Foundation status \u2014 types + factory are complete, but provider adapters are stub-level and require external credentials.";case"wip":return"WIP \u2014 actively being built, should not be used in production.";case"deprecated":return"Deprecated \u2014 scheduled for removal, do not use."}}function fn(e){let t=[];for(let{flag:o,provider:n}of e){if(!n||n==="none")continue;let r=vr(n);r!=="stable"&&t.push({flag:o,provider:n,status:r})}return t}function _r(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function yn(e){let t=_r(),o=t?"\u2693":"-",n=t?"\u25B8":"->",r=t?"\u2605":"*",s=t?"\u{1F4E7}":"*",c=["",` ${t?P.bold(`${o} Done in ${e.elapsedSec}s \xB7 ${e.targetDir}/`):`${o} Done in ${e.elapsedSec}s \xB7 ${e.targetDir}/`}`,"",` ${t?P.bold("Next:"):"Next:"}`,` ${n} cd ${e.targetDir}`];e.skippedInstall&&c.push(` ${n} pnpm install (if --no-install was set)`),c.push(` ${n} fill in .env.local ${t?P.dim("\u2192 add provider keys before booting apps"):"-> add provider keys before booting apps"}`,` ${n} pnpm db:migrate ${t?P.dim("\u2192 sync Prisma schema"):"-> sync Prisma schema"}`,` ${n} pnpm db:seed ${t?P.dim("\u2192 load example tenants and records"):"-> load example tenants and records"}`,` ${n} pnpm dev ${t?P.dim("\u2192 http://localhost:3000"):"-> http://localhost:3000"}`,"",` ${t?P.bold("Customize:"):"Customize:"}`,` ${n} pnpm brand:init ${t?P.dim("\u2192 create brand.config.ts"):"-> create brand.config.ts"}`,` ${n} pnpm brand:apply ${t?P.dim("\u2192 propagate brand changes"):"-> propagate brand changes"}`,` ${n} pnpm preset:env ${t?P.dim("\u2192 review preset env requirements"):"-> review preset env requirements"}`,"",` ${t?P.bold("Useful scripts:"):"Useful scripts:"}`,` ${n} pnpm infra:up ${t?P.dim("\u2192 start local PostgreSQL/Redis/ClickHouse"):"-> start local PostgreSQL/Redis/ClickHouse"}`,` ${n} pnpm generate:api-types ${t?P.dim("\u2192 refresh shared API client types"):"-> refresh shared API client types"}`,` ${n} pnpm lint ${t?P.dim("\u2192 quick repo sanity check"):"-> quick repo sanity check"}`,` ${n} pnpm audit ${t?P.dim("\u2192 scan deps for known CVEs"):"-> scan deps for known CVEs"}`);let a=e.previewSelections??[];if(a.length>0){let T=t?P.bold(P.yellow("\u26A0 Preview features selected:")):"!! Preview features selected:";c.push("",` ${T}`);for(let N of a){let D=Qe(N.status),ie=` ${n} ${N.flag} (${N.provider}) ${D}`;c.push(t?P.yellow(ie):ie)}c.push("",` ${t?P.dim(ce("foundation")):ce("foundation")}`,` ${t?P.dim("You may need to contribute the provider integration yourself."):"You may need to contribute the provider integration yourself."}`,` ${t?P.dim("See: https://github.com/Nebutra/Nebutra-Sailor/blob/main/docs/package-status.md"):"See: https://github.com/Nebutra/Nebutra-Sailor/blob/main/docs/package-status.md"}`)}let l=e.waveFeatures??{},m=T=>T!==!1,S=[];m(l.apiKeys)&&S.push("Manage API keys at /settings/api-keys"),m(l.auditLog)&&S.push("View audit log at /settings/audit-log"),S.push("Configure webhooks at /settings/webhooks"),m(l.commandPalette)&&S.push("Press Cmd/Ctrl+K to open the command palette"),S.push("Edit notification preferences at /settings/notifications"),S.push("Export your data at /settings/account/export"),l.chinaCompliance&&S.push("China deployments \u2014 see packages/ops/china-compliance/README.md"),c.push("",` ${t?P.bold("What you can do next:"):"What you can do next:"}`);for(let T of S)c.push(` ${n} ${T}`);c.push("",` ${t?P.bold("Ship faster:"):"Ship faster:"}`,` ${r} Star us https://github.com/nebutra/nebutra-sailor`,` ${s} Free license https://nebutra.com/get-license`,""),process.stdout.write(c.join(`
|
|
6
6
|
`)+`
|
|
7
|
-
`)}var
|
|
7
|
+
`)}var br=`Usage: create-sailor [name] [options]
|
|
8
8
|
|
|
9
9
|
Arguments:
|
|
10
10
|
name project directory (default: ./my-saas-app)
|
|
@@ -73,9 +73,9 @@ Examples:
|
|
|
73
73
|
$ npx create-sailor@latest my-app --region=cn --auth=clerk
|
|
74
74
|
$ npx create-sailor@latest my-app --region=hybrid --ai=openai,deepseek
|
|
75
75
|
$ npm create sailor@latest --dry-run --region=cn
|
|
76
|
-
`;function
|
|
77
|
-
`)}var Kt=[{id:"anthropic",name:"Anthropic",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.anthropic.com",envVarPrefix:"ANTHROPIC",requiredEnvVars:["ANTHROPIC_API_KEY"]},{id:"openai",name:"OpenAI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.openai.com/docs",envVarPrefix:"OPENAI",requiredEnvVars:["OPENAI_API_KEY"]},{id:"deepseek",name:"DeepSeek",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.deepseek.com/api-docs",envVarPrefix:"DEEPSEEK",requiredEnvVars:["DEEPSEEK_API_KEY"]},{id:"xai",name:"xAI (Grok)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.x.ai/api",envVarPrefix:"XAI",requiredEnvVars:["XAI_API_KEY"]},{id:"moonshot",name:"Moonshot (Kimi)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.moonshot.cn/docs",envVarPrefix:"MOONSHOT",requiredEnvVars:["MOONSHOT_API_KEY","MOONSHOT_BASE_URL"]},{id:"google",name:"Google Gemini",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://ai.google.dev/gemini-api/docs",envVarPrefix:"GOOGLE_GENERATIVE_AI",requiredEnvVars:["GOOGLE_GENERATIVE_AI_API_KEY"]},{id:"mistral",name:"Mistral AI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.mistral.ai",envVarPrefix:"MISTRAL",requiredEnvVars:["MISTRAL_API_KEY"]},{id:"cohere",name:"Cohere",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.cohere.com",envVarPrefix:"COHERE",requiredEnvVars:["COHERE_API_KEY"]},{id:"perplexity",name:"Perplexity",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.perplexity.ai",baseURL:"https://api.perplexity.ai",envVarPrefix:"PERPLEXITY",requiredEnvVars:["PERPLEXITY_API_KEY"]},{id:"ai21",name:"AI21 Labs",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.ai21.com",baseURL:"https://api.ai21.com/studio/v1",envVarPrefix:"AI21",requiredEnvVars:["AI21_API_KEY"]},{id:"upstage",name:"Upstage (Solar)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://developers.upstage.ai/docs",baseURL:"https://api.upstage.ai/v1/solar",envVarPrefix:"UPSTAGE",requiredEnvVars:["UPSTAGE_API_KEY"]},{id:"siliconflow",name:"\u7845\u57FA\u6D41\u52A8 SiliconFlow",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://docs.siliconflow.cn",baseURL:"https://api.siliconflow.cn/v1",envVarPrefix:"SILICONFLOW",requiredEnvVars:["SILICONFLOW_API_KEY"]},{id:"volcengine-ark",name:"\u706B\u5C71\u5F15\u64CE ARK (\u8C46\u5305)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://www.volcengine.com/docs/82379",baseURL:"https://ark.cn-beijing.volces.com/api/v3",envVarPrefix:"ARK",requiredEnvVars:["ARK_API_KEY"]},{id:"bailian",name:"\u767E\u70BC Bailian (\u901A\u4E49)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://help.aliyun.com/zh/model-studio",baseURL:"https://dashscope.aliyuncs.com/compatible-mode/v1",envVarPrefix:"DASHSCOPE",requiredEnvVars:["DASHSCOPE_API_KEY"]},{id:"zhipu",name:"\u667A\u8C31 AI Zhipu",category:"\u56FD\u5185\u5E73\u53F0",status:"ai-sdk",docs:"https://docs.bigmodel.cn",baseURL:"https://open.bigmodel.cn/api/paas/v4",envVarPrefix:"ZHIPU",requiredEnvVars:["ZHIPU_API_KEY"]},{id:"baichuan",name:"\u767E\u5DDD\u667A\u80FD Baichuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.baichuan-ai.com/docs",baseURL:"https://api.baichuan-ai.com/v1",envVarPrefix:"BAICHUAN",requiredEnvVars:["BAICHUAN_API_KEY"]},{id:"minimax",name:"MiniMax (ABAB)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.minimaxi.com/document/guides",baseURL:"https://api.minimax.chat/v1",envVarPrefix:"MINIMAX",requiredEnvVars:["MINIMAX_API_KEY","MINIMAX_GROUP_ID"]},{id:"stepfun",name:"\u9636\u8DC3\u661F\u8FB0 StepFun",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.stepfun.com/docs",baseURL:"https://api.stepfun.com/v1",envVarPrefix:"STEPFUN",requiredEnvVars:["STEPFUN_API_KEY"]},{id:"sensetime",name:"\u5546\u6C64 SenseNova",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.sensetime.com",baseURL:"https://api.sensenova.cn/v1",envVarPrefix:"SENSENOVA",requiredEnvVars:["SENSENOVA_API_KEY"]},{id:"tencent",name:"\u817E\u8BAF\u6DF7\u5143 Hunyuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://cloud.tencent.com/document/product/1729",baseURL:"https://api.hunyuan.cloud.tencent.com/v1",envVarPrefix:"HUNYUAN",requiredEnvVars:["HUNYUAN_API_KEY"]},{id:"lingyi",name:"\u96F6\u4E00\u4E07\u7269 Yi",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.lingyiwanwu.com/docs",baseURL:"https://api.lingyiwanwu.com/v1",envVarPrefix:"YI",requiredEnvVars:["YI_API_KEY"]},{id:"openrouter",name:"OpenRouter",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://openrouter.ai/docs",envVarPrefix:"OPENROUTER",requiredEnvVars:["OPENROUTER_API_KEY"]},{id:"vercel-gateway",name:"Vercel AI Gateway",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://vercel.com/docs/ai-gateway",envVarPrefix:"AI_GATEWAY",requiredEnvVars:["AI_GATEWAY_API_KEY"]},{id:"litellm",name:"LiteLLM Proxy",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://docs.litellm.ai",baseURL:"http://localhost:4000/v1",envVarPrefix:"LITELLM",requiredEnvVars:["LITELLM_API_KEY"]},{id:"portkey",name:"Portkey",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://portkey.ai/docs",baseURL:"https://api.portkey.ai/v1",envVarPrefix:"PORTKEY",requiredEnvVars:["PORTKEY_API_KEY"]},{id:"aws-bedrock",name:"AWS Bedrock",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://aws.amazon.com/bedrock/",envVarPrefix:"AWS_BEDROCK",requiredEnvVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"]},{id:"azure-openai",name:"Azure OpenAI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/",envVarPrefix:"AZURE_OPENAI",requiredEnvVars:["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"]},{id:"gcp-vertex",name:"GCP Vertex AI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://cloud.google.com/vertex-ai",envVarPrefix:"GOOGLE_VERTEX",requiredEnvVars:["GOOGLE_APPLICATION_CREDENTIALS"]},{id:"groq",name:"Groq",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://console.groq.com/docs",envVarPrefix:"GROQ",requiredEnvVars:["GROQ_API_KEY"]},{id:"fireworks",name:"Fireworks AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.fireworks.ai",envVarPrefix:"FIREWORKS",requiredEnvVars:["FIREWORKS_API_KEY"]},{id:"together",name:"Together AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.together.ai",envVarPrefix:"TOGETHER",requiredEnvVars:["TOGETHER_API_KEY"]},{id:"huggingface",name:"Hugging Face",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://huggingface.co/docs/inference-providers",envVarPrefix:"HF",requiredEnvVars:["HF_API_KEY"]},{id:"replicate",name:"Replicate",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://replicate.com/docs",envVarPrefix:"REPLICATE",requiredEnvVars:["REPLICATE_API_TOKEN"]},{id:"lepton",name:"Lepton AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://www.lepton.ai/docs",baseURL:"https://api.lepton.ai/v1",envVarPrefix:"LEPTON",requiredEnvVars:["LEPTON_API_KEY"]},{id:"anyscale",name:"Anyscale Endpoints",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://docs.endpoints.anyscale.com/",baseURL:"https://api.endpoints.anyscale.com/v1",envVarPrefix:"ANYSCALE",requiredEnvVars:["ANYSCALE_API_KEY"]},{id:"octoai",name:"OctoAI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://octoai.cloud/docs",baseURL:"https://text.octoai.run/v1",envVarPrefix:"OCTOAI",requiredEnvVars:["OCTOAI_TOKEN"]},{id:"deepinfra",name:"DeepInfra",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://deepinfra.com/docs",baseURL:"https://api.deepinfra.com/v1/openai",envVarPrefix:"DEEPINFRA",requiredEnvVars:["DEEPINFRA_API_KEY"]},{id:"novita",name:"Novita AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://novita.ai/docs",baseURL:"https://api.novita.ai/v3/openai",envVarPrefix:"NOVITA",requiredEnvVars:["NOVITA_API_KEY"]},{id:"black-forest-labs",name:"Black Forest Labs (FLUX)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://docs.bfl.ml",envVarPrefix:"BFL",requiredEnvVars:["BFL_API_KEY"]},{id:"elevenlabs",name:"ElevenLabs (TTS)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://elevenlabs.io/docs",envVarPrefix:"ELEVENLABS",requiredEnvVars:["ELEVENLABS_API_KEY"]},{id:"runway",name:"Runway (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://runwayml.com",envVarPrefix:"RUNWAY",requiredEnvVars:["RUNWAY_API_KEY"]},{id:"luma",name:"Luma (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://lumalabs.ai",envVarPrefix:"LUMA",requiredEnvVars:["LUMA_API_KEY"]},{id:"midjourney",name:"Midjourney",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://docs.midjourney.com",envVarPrefix:"MIDJOURNEY",requiredEnvVars:[]},{id:"ollama",name:"Ollama (\u672C\u5730)",category:"\u672C\u5730\u90E8\u7F72",status:"opencode",docs:"https://github.com/ollama/ollama",baseURL:"http://localhost:11434/v1",envVarPrefix:"OLLAMA",requiredEnvVars:["OLLAMA_BASE_URL"]},{id:"lmstudio",name:"LM Studio",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://lmstudio.ai/docs",baseURL:"http://localhost:1234/v1",envVarPrefix:"LMSTUDIO",requiredEnvVars:[]},{id:"vllm",name:"vLLM",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://docs.vllm.ai",baseURL:"http://localhost:8000/v1",envVarPrefix:"VLLM",requiredEnvVars:[]},{id:"tgi",name:"HF TGI",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://huggingface.co/docs/text-generation-inference",baseURL:"http://localhost:8080/v1",envVarPrefix:"TGI",requiredEnvVars:[]}],ba=Kt.reduce((e,t)=>(e[t.category]||(e[t.category]=[]),e[t.category].push(t),e),{});var ur=["openai","anthropic","google"],mr=["openai"];function Vt(e){let t=e.providerIds??[];switch(e.mode){case"gateway":{let o=t.length>0?t:[...ur];return{mode:"gateway",providerIds:o,customEndpoint:e.customEndpoint,routing:{profile:"multi-provider-gateway",providerSeed:o,runtimeGovernance:!0}}}case"direct":{let o=t.length>0?t:[...mr];return{mode:"direct",providerIds:o,customEndpoint:e.customEndpoint,routing:{profile:"direct-adapters",providerSeed:o,runtimeGovernance:!1}}}case"custom":return{mode:"custom",providerIds:t,customEndpoint:e.customEndpoint,routing:{profile:"openai-compatible",providerSeed:t,runtimeGovernance:!0}};case"none":return{mode:"none",providerIds:[],routing:{profile:"disabled",providerSeed:[],runtimeGovernance:!1}}}}import Ze from"fs";import D from"path";var Sn=[{id:"posthog",name:"PostHog",region:"both",envVars:["POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_HOST"],docs:"https://posthog.com/docs",pkgs:["posthog-js","posthog-node"],providerFile:"apps/web/src/lib/analytics/posthog.client.ts"},{id:"plausible",name:"Plausible",region:"global",envVars:["NEXT_PUBLIC_PLAUSIBLE_DOMAIN","NEXT_PUBLIC_PLAUSIBLE_SRC"],docs:"https://plausible.io/docs",providerFile:"apps/web/src/components/analytics/PlausibleScript.tsx"},{id:"umami",name:"Umami",region:"both",envVars:["NEXT_PUBLIC_UMAMI_WEBSITE_ID","NEXT_PUBLIC_UMAMI_SRC"],docs:"https://umami.is/docs",providerFile:"apps/web/src/components/analytics/UmamiScript.tsx"},{id:"mixpanel",name:"Mixpanel",region:"global",envVars:["MIXPANEL_TOKEN","NEXT_PUBLIC_MIXPANEL_TOKEN"],docs:"https://docs.mixpanel.com",pkgs:["mixpanel-browser","mixpanel"],providerFile:"apps/web/src/lib/analytics/mixpanel.client.ts"},{id:"baidu",name:"\u767E\u5EA6\u7EDF\u8BA1 Baidu Tongji",region:"cn",envVars:["NEXT_PUBLIC_BAIDU_STATS_ID"],docs:"https://tongji.baidu.com",providerFile:"apps/web/src/components/analytics/BaiduStatsScript.tsx"},{id:"sensors",name:"\u795E\u7B56 Sensors Analytics",region:"cn",envVars:["NEXT_PUBLIC_SENSORS_SERVER_URL","NEXT_PUBLIC_SENSORS_APP_JS_URL"],docs:"https://manual.sensorsdata.cn",pkgs:["sa-sdk-javascript"],providerFile:"apps/web/src/lib/analytics/sensors.client.ts"},{id:"growingio",name:"GrowingIO",region:"cn",envVars:["NEXT_PUBLIC_GROWINGIO_ACCOUNT_ID"],docs:"https://growingio.com/docs",providerFile:"apps/web/src/components/analytics/GrowingIoScript.tsx"},{id:"none",name:"None",region:"both",envVars:[],docs:""}],Ia=Sn.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function hn(e){return Sn.find(t=>t.id===e)}function gr(e){Ze.mkdirSync(e,{recursive:!0})}function W(e,t){gr(D.dirname(e)),Ze.writeFileSync(e,t)}function _e(e,t){let o=D.join(e,".env.example");Ze.existsSync(o)?Ze.appendFileSync(o,`
|
|
78
|
-
`+t):Ze.writeFileSync(o,t)}function
|
|
76
|
+
`;function Sn(){process.stdout.write(br)}import wt from"picocolors";function hn(){return!process.env.NO_COLOR&&!!process.stdout.isTTY}function Pr(e){let t=hn()?"\u25B8":"->",o=`[${e.index}/${e.total}]`,n=e.label.padEnd(18," ");return hn()?`${wt.dim(o)} ${wt.bold(n)} ${wt.dim(t)} ${wt.cyan(e.value)}`:`${o} ${n} ${t} ${e.value}`}function En(e){process.stdout.write(Pr(e)+`
|
|
77
|
+
`)}var Ht=[{id:"anthropic",name:"Anthropic",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.anthropic.com",envVarPrefix:"ANTHROPIC",requiredEnvVars:["ANTHROPIC_API_KEY"]},{id:"openai",name:"OpenAI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.openai.com/docs",envVarPrefix:"OPENAI",requiredEnvVars:["OPENAI_API_KEY"]},{id:"deepseek",name:"DeepSeek",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.deepseek.com/api-docs",envVarPrefix:"DEEPSEEK",requiredEnvVars:["DEEPSEEK_API_KEY"]},{id:"xai",name:"xAI (Grok)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://docs.x.ai/api",envVarPrefix:"XAI",requiredEnvVars:["XAI_API_KEY"]},{id:"moonshot",name:"Moonshot (Kimi)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"opencode",docs:"https://platform.moonshot.cn/docs",envVarPrefix:"MOONSHOT",requiredEnvVars:["MOONSHOT_API_KEY","MOONSHOT_BASE_URL"]},{id:"google",name:"Google Gemini",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://ai.google.dev/gemini-api/docs",envVarPrefix:"GOOGLE_GENERATIVE_AI",requiredEnvVars:["GOOGLE_GENERATIVE_AI_API_KEY"]},{id:"mistral",name:"Mistral AI",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.mistral.ai",envVarPrefix:"MISTRAL",requiredEnvVars:["MISTRAL_API_KEY"]},{id:"cohere",name:"Cohere",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"ai-sdk",docs:"https://docs.cohere.com",envVarPrefix:"COHERE",requiredEnvVars:["COHERE_API_KEY"]},{id:"perplexity",name:"Perplexity",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.perplexity.ai",baseURL:"https://api.perplexity.ai",envVarPrefix:"PERPLEXITY",requiredEnvVars:["PERPLEXITY_API_KEY"]},{id:"ai21",name:"AI21 Labs",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://docs.ai21.com",baseURL:"https://api.ai21.com/studio/v1",envVarPrefix:"AI21",requiredEnvVars:["AI21_API_KEY"]},{id:"upstage",name:"Upstage (Solar)",category:"\u76F4\u63A5\u5B9E\u9A8C\u5BA4",status:"cn-compatible",docs:"https://developers.upstage.ai/docs",baseURL:"https://api.upstage.ai/v1/solar",envVarPrefix:"UPSTAGE",requiredEnvVars:["UPSTAGE_API_KEY"]},{id:"siliconflow",name:"\u7845\u57FA\u6D41\u52A8 SiliconFlow",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://docs.siliconflow.cn",baseURL:"https://api.siliconflow.cn/v1",envVarPrefix:"SILICONFLOW",requiredEnvVars:["SILICONFLOW_API_KEY"]},{id:"volcengine-ark",name:"\u706B\u5C71\u5F15\u64CE ARK (\u8C46\u5305)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://www.volcengine.com/docs/82379",baseURL:"https://ark.cn-beijing.volces.com/api/v3",envVarPrefix:"ARK",requiredEnvVars:["ARK_API_KEY"]},{id:"bailian",name:"\u767E\u70BC Bailian (\u901A\u4E49)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://help.aliyun.com/zh/model-studio",baseURL:"https://dashscope.aliyuncs.com/compatible-mode/v1",envVarPrefix:"DASHSCOPE",requiredEnvVars:["DASHSCOPE_API_KEY"]},{id:"zhipu",name:"\u667A\u8C31 AI Zhipu",category:"\u56FD\u5185\u5E73\u53F0",status:"ai-sdk",docs:"https://docs.bigmodel.cn",baseURL:"https://open.bigmodel.cn/api/paas/v4",envVarPrefix:"ZHIPU",requiredEnvVars:["ZHIPU_API_KEY"]},{id:"baichuan",name:"\u767E\u5DDD\u667A\u80FD Baichuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.baichuan-ai.com/docs",baseURL:"https://api.baichuan-ai.com/v1",envVarPrefix:"BAICHUAN",requiredEnvVars:["BAICHUAN_API_KEY"]},{id:"minimax",name:"MiniMax (ABAB)",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.minimaxi.com/document/guides",baseURL:"https://api.minimax.chat/v1",envVarPrefix:"MINIMAX",requiredEnvVars:["MINIMAX_API_KEY","MINIMAX_GROUP_ID"]},{id:"stepfun",name:"\u9636\u8DC3\u661F\u8FB0 StepFun",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.stepfun.com/docs",baseURL:"https://api.stepfun.com/v1",envVarPrefix:"STEPFUN",requiredEnvVars:["STEPFUN_API_KEY"]},{id:"sensetime",name:"\u5546\u6C64 SenseNova",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.sensetime.com",baseURL:"https://api.sensenova.cn/v1",envVarPrefix:"SENSENOVA",requiredEnvVars:["SENSENOVA_API_KEY"]},{id:"tencent",name:"\u817E\u8BAF\u6DF7\u5143 Hunyuan",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://cloud.tencent.com/document/product/1729",baseURL:"https://api.hunyuan.cloud.tencent.com/v1",envVarPrefix:"HUNYUAN",requiredEnvVars:["HUNYUAN_API_KEY"]},{id:"lingyi",name:"\u96F6\u4E00\u4E07\u7269 Yi",category:"\u56FD\u5185\u5E73\u53F0",status:"cn-compatible",docs:"https://platform.lingyiwanwu.com/docs",baseURL:"https://api.lingyiwanwu.com/v1",envVarPrefix:"YI",requiredEnvVars:["YI_API_KEY"]},{id:"openrouter",name:"OpenRouter",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://openrouter.ai/docs",envVarPrefix:"OPENROUTER",requiredEnvVars:["OPENROUTER_API_KEY"]},{id:"vercel-gateway",name:"Vercel AI Gateway",category:"\u7EDF\u4E00\u7F51\u5173",status:"opencode",docs:"https://vercel.com/docs/ai-gateway",envVarPrefix:"AI_GATEWAY",requiredEnvVars:["AI_GATEWAY_API_KEY"]},{id:"litellm",name:"LiteLLM Proxy",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://docs.litellm.ai",baseURL:"http://localhost:4000/v1",envVarPrefix:"LITELLM",requiredEnvVars:["LITELLM_API_KEY"]},{id:"portkey",name:"Portkey",category:"\u7EDF\u4E00\u7F51\u5173",status:"cn-compatible",docs:"https://portkey.ai/docs",baseURL:"https://api.portkey.ai/v1",envVarPrefix:"PORTKEY",requiredEnvVars:["PORTKEY_API_KEY"]},{id:"aws-bedrock",name:"AWS Bedrock",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://aws.amazon.com/bedrock/",envVarPrefix:"AWS_BEDROCK",requiredEnvVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"]},{id:"azure-openai",name:"Azure OpenAI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://azure.microsoft.com/en-us/products/cognitive-services/openai-service/",envVarPrefix:"AZURE_OPENAI",requiredEnvVars:["AZURE_OPENAI_API_KEY","AZURE_OPENAI_ENDPOINT"]},{id:"gcp-vertex",name:"GCP Vertex AI",category:"\u4E91\u5E73\u53F0",status:"ai-sdk",docs:"https://cloud.google.com/vertex-ai",envVarPrefix:"GOOGLE_VERTEX",requiredEnvVars:["GOOGLE_APPLICATION_CREDENTIALS"]},{id:"groq",name:"Groq",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://console.groq.com/docs",envVarPrefix:"GROQ",requiredEnvVars:["GROQ_API_KEY"]},{id:"fireworks",name:"Fireworks AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.fireworks.ai",envVarPrefix:"FIREWORKS",requiredEnvVars:["FIREWORKS_API_KEY"]},{id:"together",name:"Together AI",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://docs.together.ai",envVarPrefix:"TOGETHER",requiredEnvVars:["TOGETHER_API_KEY"]},{id:"huggingface",name:"Hugging Face",category:"\u63A8\u7406\u52A0\u901F",status:"opencode",docs:"https://huggingface.co/docs/inference-providers",envVarPrefix:"HF",requiredEnvVars:["HF_API_KEY"]},{id:"replicate",name:"Replicate",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://replicate.com/docs",envVarPrefix:"REPLICATE",requiredEnvVars:["REPLICATE_API_TOKEN"]},{id:"lepton",name:"Lepton AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://www.lepton.ai/docs",baseURL:"https://api.lepton.ai/v1",envVarPrefix:"LEPTON",requiredEnvVars:["LEPTON_API_KEY"]},{id:"anyscale",name:"Anyscale Endpoints",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://docs.endpoints.anyscale.com/",baseURL:"https://api.endpoints.anyscale.com/v1",envVarPrefix:"ANYSCALE",requiredEnvVars:["ANYSCALE_API_KEY"]},{id:"octoai",name:"OctoAI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://octoai.cloud/docs",baseURL:"https://text.octoai.run/v1",envVarPrefix:"OCTOAI",requiredEnvVars:["OCTOAI_TOKEN"]},{id:"deepinfra",name:"DeepInfra",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://deepinfra.com/docs",baseURL:"https://api.deepinfra.com/v1/openai",envVarPrefix:"DEEPINFRA",requiredEnvVars:["DEEPINFRA_API_KEY"]},{id:"novita",name:"Novita AI",category:"\u63A8\u7406\u52A0\u901F",status:"cn-compatible",docs:"https://novita.ai/docs",baseURL:"https://api.novita.ai/v3/openai",envVarPrefix:"NOVITA",requiredEnvVars:["NOVITA_API_KEY"]},{id:"black-forest-labs",name:"Black Forest Labs (FLUX)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://docs.bfl.ml",envVarPrefix:"BFL",requiredEnvVars:["BFL_API_KEY"]},{id:"elevenlabs",name:"ElevenLabs (TTS)",category:"\u591A\u6A21\u6001",status:"ai-sdk",docs:"https://elevenlabs.io/docs",envVarPrefix:"ELEVENLABS",requiredEnvVars:["ELEVENLABS_API_KEY"]},{id:"runway",name:"Runway (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://runwayml.com",envVarPrefix:"RUNWAY",requiredEnvVars:["RUNWAY_API_KEY"]},{id:"luma",name:"Luma (Video)",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://lumalabs.ai",envVarPrefix:"LUMA",requiredEnvVars:["LUMA_API_KEY"]},{id:"midjourney",name:"Midjourney",category:"\u591A\u6A21\u6001",status:"pending",docs:"https://docs.midjourney.com",envVarPrefix:"MIDJOURNEY",requiredEnvVars:[]},{id:"ollama",name:"Ollama (\u672C\u5730)",category:"\u672C\u5730\u90E8\u7F72",status:"opencode",docs:"https://github.com/ollama/ollama",baseURL:"http://localhost:11434/v1",envVarPrefix:"OLLAMA",requiredEnvVars:["OLLAMA_BASE_URL"]},{id:"lmstudio",name:"LM Studio",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://lmstudio.ai/docs",baseURL:"http://localhost:1234/v1",envVarPrefix:"LMSTUDIO",requiredEnvVars:[]},{id:"vllm",name:"vLLM",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://docs.vllm.ai",baseURL:"http://localhost:8000/v1",envVarPrefix:"VLLM",requiredEnvVars:[]},{id:"tgi",name:"HF TGI",category:"\u672C\u5730\u90E8\u7F72",status:"cn-compatible",docs:"https://huggingface.co/docs/text-generation-inference",baseURL:"http://localhost:8080/v1",envVarPrefix:"TGI",requiredEnvVars:[]}],Ba=Ht.reduce((e,t)=>(e[t.category]||(e[t.category]=[]),e[t.category].push(t),e),{});var Cr=["openai","anthropic","google"],Ir=["openai"];function Wt(e){let t=e.providerIds??[];switch(e.mode){case"gateway":{let o=t.length>0?t:[...Cr];return{mode:"gateway",providerIds:o,customEndpoint:e.customEndpoint,routing:{profile:"multi-provider-gateway",providerSeed:o,runtimeGovernance:!0}}}case"direct":{let o=t.length>0?t:[...Ir];return{mode:"direct",providerIds:o,customEndpoint:e.customEndpoint,routing:{profile:"direct-adapters",providerSeed:o,runtimeGovernance:!1}}}case"custom":return{mode:"custom",providerIds:t,customEndpoint:e.customEndpoint,routing:{profile:"openai-compatible",providerSeed:t,runtimeGovernance:!0}};case"none":return{mode:"none",providerIds:[],routing:{profile:"disabled",providerSeed:[],runtimeGovernance:!1}}}}import Ze from"fs";import j from"path";var vn=[{id:"posthog",name:"PostHog",region:"both",envVars:["POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_KEY","NEXT_PUBLIC_POSTHOG_HOST"],docs:"https://posthog.com/docs",pkgs:["posthog-js","posthog-node"],providerFile:"apps/web/src/lib/analytics/posthog.client.ts"},{id:"plausible",name:"Plausible",region:"global",envVars:["NEXT_PUBLIC_PLAUSIBLE_DOMAIN","NEXT_PUBLIC_PLAUSIBLE_SRC"],docs:"https://plausible.io/docs",providerFile:"apps/web/src/components/analytics/PlausibleScript.tsx"},{id:"umami",name:"Umami",region:"both",envVars:["NEXT_PUBLIC_UMAMI_WEBSITE_ID","NEXT_PUBLIC_UMAMI_SRC"],docs:"https://umami.is/docs",providerFile:"apps/web/src/components/analytics/UmamiScript.tsx"},{id:"mixpanel",name:"Mixpanel",region:"global",envVars:["MIXPANEL_TOKEN","NEXT_PUBLIC_MIXPANEL_TOKEN"],docs:"https://docs.mixpanel.com",pkgs:["mixpanel-browser","mixpanel"],providerFile:"apps/web/src/lib/analytics/mixpanel.client.ts"},{id:"baidu",name:"\u767E\u5EA6\u7EDF\u8BA1 Baidu Tongji",region:"cn",envVars:["NEXT_PUBLIC_BAIDU_STATS_ID"],docs:"https://tongji.baidu.com",providerFile:"apps/web/src/components/analytics/BaiduStatsScript.tsx"},{id:"sensors",name:"\u795E\u7B56 Sensors Analytics",region:"cn",envVars:["NEXT_PUBLIC_SENSORS_SERVER_URL","NEXT_PUBLIC_SENSORS_APP_JS_URL"],docs:"https://manual.sensorsdata.cn",pkgs:["sa-sdk-javascript"],providerFile:"apps/web/src/lib/analytics/sensors.client.ts"},{id:"growingio",name:"GrowingIO",region:"cn",envVars:["NEXT_PUBLIC_GROWINGIO_ACCOUNT_ID"],docs:"https://growingio.com/docs",providerFile:"apps/web/src/components/analytics/GrowingIoScript.tsx"},{id:"none",name:"None",region:"both",envVars:[],docs:""}],qa=vn.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function _n(e){return vn.find(t=>t.id===e)}function Ar(e){Ze.mkdirSync(e,{recursive:!0})}function z(e,t){Ar(j.dirname(e)),Ze.writeFileSync(e,t)}function he(e,t){let o=j.join(e,".env.example");Ze.existsSync(o)?Ze.appendFileSync(o,`
|
|
78
|
+
`+t):Ze.writeFileSync(o,t)}function wr(e){let t=`"use client";
|
|
79
79
|
/**
|
|
80
80
|
* PostHog browser SDK \u2014 lazy-initialized on first access.
|
|
81
81
|
*/
|
|
@@ -158,11 +158,11 @@ export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
|
|
|
158
158
|
|
|
159
159
|
return <>{children}</>;
|
|
160
160
|
}
|
|
161
|
-
`;
|
|
161
|
+
`;z(j.join(e,"src","lib","analytics","posthog.client.ts"),t),z(j.join(e,"src","lib","analytics","posthog.server.ts"),o),z(j.join(e,"src","components","analytics","AnalyticsProvider.tsx"),n)}function Rr(){return`# PostHog \u2014 product analytics
|
|
162
162
|
POSTHOG_KEY=
|
|
163
163
|
NEXT_PUBLIC_POSTHOG_KEY=
|
|
164
164
|
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
|
|
165
|
-
`}function
|
|
165
|
+
`}function kr(e){z(j.join(e,"src","components","analytics","PlausibleScript.tsx"),`"use client";
|
|
166
166
|
/**
|
|
167
167
|
* Plausible script tag \u2014 lightweight, privacy-friendly analytics.
|
|
168
168
|
*/
|
|
@@ -182,10 +182,10 @@ export function PlausibleScript() {
|
|
|
182
182
|
/>
|
|
183
183
|
);
|
|
184
184
|
}
|
|
185
|
-
`)}function
|
|
185
|
+
`)}function xr(){return`# Plausible \u2014 privacy-friendly analytics
|
|
186
186
|
NEXT_PUBLIC_PLAUSIBLE_DOMAIN=
|
|
187
187
|
NEXT_PUBLIC_PLAUSIBLE_SRC=https://plausible.io/js/script.js
|
|
188
|
-
`}function
|
|
188
|
+
`}function Tr(e){z(j.join(e,"src","components","analytics","UmamiScript.tsx"),`"use client";
|
|
189
189
|
/**
|
|
190
190
|
* Umami analytics \u2014 self-hosted OR umami.is cloud.
|
|
191
191
|
*/
|
|
@@ -205,10 +205,10 @@ export function UmamiScript() {
|
|
|
205
205
|
/>
|
|
206
206
|
);
|
|
207
207
|
}
|
|
208
|
-
`)}function
|
|
208
|
+
`)}function Nr(){return`# Umami \u2014 self-hostable analytics
|
|
209
209
|
NEXT_PUBLIC_UMAMI_WEBSITE_ID=
|
|
210
210
|
NEXT_PUBLIC_UMAMI_SRC=https://cloud.umami.is/script.js
|
|
211
|
-
`}function
|
|
211
|
+
`}function Or(e){let t=`"use client";
|
|
212
212
|
import mixpanel from "mixpanel-browser";
|
|
213
213
|
|
|
214
214
|
let initialized = false;
|
|
@@ -247,10 +247,10 @@ export function trackServer(
|
|
|
247
247
|
if (!c) return;
|
|
248
248
|
c.track(event, { distinct_id: distinctId, ...properties });
|
|
249
249
|
}
|
|
250
|
-
`;
|
|
250
|
+
`;z(j.join(e,"src","lib","analytics","mixpanel.client.ts"),t),z(j.join(e,"src","lib","analytics","mixpanel.server.ts"),o)}function Ur(){return`# Mixpanel \u2014 product analytics
|
|
251
251
|
MIXPANEL_TOKEN=
|
|
252
252
|
NEXT_PUBLIC_MIXPANEL_TOKEN=
|
|
253
|
-
`}function
|
|
253
|
+
`}function Lr(e){z(j.join(e,"src","components","analytics","BaiduStatsScript.tsx"),`"use client";
|
|
254
254
|
/**
|
|
255
255
|
* \u767E\u5EA6\u7EDF\u8BA1 Baidu Tongji \u2014 hm.js \u57CB\u70B9\u811A\u672C.
|
|
256
256
|
*/
|
|
@@ -276,9 +276,9 @@ var _hmt = _hmt || [];
|
|
|
276
276
|
/>
|
|
277
277
|
);
|
|
278
278
|
}
|
|
279
|
-
`)}function
|
|
279
|
+
`)}function Mr(){return`# \u767E\u5EA6\u7EDF\u8BA1 \u2014 \u4E2D\u56FD\u533A\u7F51\u7AD9\u5206\u6790
|
|
280
280
|
NEXT_PUBLIC_BAIDU_STATS_ID=
|
|
281
|
-
`}function
|
|
281
|
+
`}function Dr(e){z(j.join(e,"src","lib","analytics","sensors.client.ts"),`"use client";
|
|
282
282
|
/**
|
|
283
283
|
* \u795E\u7B56\u5206\u6790 (Sensors Analytics) \u2014 sa-sdk-javascript \u5C01\u88C5.
|
|
284
284
|
*/
|
|
@@ -306,10 +306,10 @@ export function initSensors(): void {
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
export { sensors };
|
|
309
|
-
`)}function
|
|
309
|
+
`)}function Fr(){return`# \u795E\u7B56\u5206\u6790 Sensors Analytics
|
|
310
310
|
NEXT_PUBLIC_SENSORS_SERVER_URL=
|
|
311
311
|
NEXT_PUBLIC_SENSORS_APP_JS_URL=
|
|
312
|
-
`}function
|
|
312
|
+
`}function jr(e){z(j.join(e,"src","components","analytics","GrowingIoScript.tsx"),`"use client";
|
|
313
313
|
/**
|
|
314
314
|
* GrowingIO CDP \u2014 inline snippet installer.
|
|
315
315
|
*/
|
|
@@ -334,21 +334,21 @@ gdp("send");
|
|
|
334
334
|
/>
|
|
335
335
|
);
|
|
336
336
|
}
|
|
337
|
-
`)}function
|
|
337
|
+
`)}function Vr(){return`# GrowingIO \u2014 \u5168\u57CB\u70B9 CDP (\u4E2D\u56FD\u533A)
|
|
338
338
|
NEXT_PUBLIC_GROWINGIO_ACCOUNT_ID=
|
|
339
|
-
`}async function
|
|
340
|
-
`+t):
|
|
341
|
-
`+t)}async function
|
|
339
|
+
`}async function bn(e,t,o="global"){let n=_n(t);if(!n||n.id==="none")return;let r=j.join(e,"apps","web");if(!Ze.existsSync(r))return;let s=n.id;try{switch(s){case"posthog":wr(r),he(e,Rr());return;case"plausible":kr(r),he(e,xr());return;case"umami":Tr(r),he(e,Nr());return;case"mixpanel":Or(r),he(e,Ur());return;case"baidu":Lr(r),he(e,Mr());return;case"sensors":Dr(r),he(e,Fr());return;case"growingio":jr(r),he(e,Vr());return;default:return}}catch(i){throw console.error(`Failed to apply analytics selection "${t}":`,i),new Error(`Analytics scaffold for "${t}" failed \u2014 see stderr for details.`)}}var Kr="https://analytics.nebutra.com";function Br(e={}){if(e.noTelemetry===!0)return!0;let t=process.env.NEBUTRA_TELEMETRY;return t==="0"||t==="false"}function Pn(e,t={}){Br(t)||(async()=>{try{let o=await import("@nebutra/analytics");if(typeof o.createAnalyticsClient!="function")return;let n=o.createAnalyticsClient({posthog:{apiKey:process.env.NEBUTRA_POSTHOG_KEY??"",host:process.env.NEBUTRA_POSTHOG_HOST??Kr},onError:()=>{}});if(typeof n?.track!="function")return;let r=n.track("scaffold.completed",e);r&&typeof r.then=="function"&&await r.catch(()=>{})}catch{}})()}import B from"fs";import G from"path";function Ee(e){B.existsSync(e)&&B.rmSync(e,{recursive:!0,force:!0})}function Xt(e,t){let o=G.join(e,".env.example");B.existsSync(o)?B.appendFileSync(o,`
|
|
340
|
+
`+t):B.writeFileSync(o,t)}function zt(e,t){if(!B.existsSync(e))return;let o=B.readFileSync(e,"utf8"),n=o.replace(/export type AuthProviderId\s*=\s*(?:"[^"]+"\s*\|\s*)+"[^"]+";/,`export type AuthProviderId = "${t}";`);n!==o&&B.writeFileSync(e,n)}function $r(e,t){let o=G.join(e,"README.md");if(!B.existsSync(o)){B.writeFileSync(o,t);return}B.appendFileSync(o,`
|
|
341
|
+
`+t)}async function Cn(e,t){let o=G.join(e,"packages","iam","auth");if(!B.existsSync(o))return;if(t==="none"){Ee(o),$r(e,`
|
|
342
342
|
## Auth
|
|
343
343
|
|
|
344
344
|
Auth was skipped at scaffold time (\`--auth=none\`). Add your preferred auth provider manually (e.g. Clerk, Better Auth).
|
|
345
|
-
`);return}let n
|
|
345
|
+
`);return}let n=G.join(o,"src","providers"),r=G.join(o,"src","types.ts");if(t==="clerk"){Ee(G.join(n,"better-auth.ts")),Ee(G.join(n,"nextauth.ts")),zt(r,"clerk"),Xt(e,`# Clerk auth
|
|
346
346
|
CLERK_PUBLISHABLE_KEY=
|
|
347
347
|
CLERK_SECRET_KEY=
|
|
348
|
-
`);return}if(t==="betterauth"){
|
|
348
|
+
`);return}if(t==="betterauth"){Ee(G.join(n,"clerk.ts")),Ee(G.join(n,"nextauth.ts")),zt(r,"better-auth"),Xt(e,`# Better Auth
|
|
349
349
|
BETTER_AUTH_SECRET=
|
|
350
350
|
BETTER_AUTH_URL=http://localhost:3000
|
|
351
|
-
`);return}if(t==="nextauth"){
|
|
351
|
+
`);return}if(t==="nextauth"){Ee(G.join(n,"clerk.ts")),Ee(G.join(n,"better-auth.ts")),zt(r,"nextauth"),Xt(e,`# NextAuth (Auth.js v5)
|
|
352
352
|
AUTH_SECRET=
|
|
353
353
|
NEXTAUTH_URL=http://localhost:3000
|
|
354
354
|
# OAuth (optional \u2014 leave blank to disable that provider)
|
|
@@ -356,7 +356,7 @@ GOOGLE_CLIENT_ID=
|
|
|
356
356
|
GOOGLE_CLIENT_SECRET=
|
|
357
357
|
GITHUB_CLIENT_ID=
|
|
358
358
|
GITHUB_CLIENT_SECRET=
|
|
359
|
-
`);return}}var
|
|
359
|
+
`);return}}var dt=[{id:"wechat",name:"\u5FAE\u4FE1\u5F00\u653E\u5E73\u53F0",region:"cn",envVars:["WECHAT_APP_ID","WECHAT_APP_SECRET"],docs:"https://open.weixin.qq.com/cgi-bin/showdocument",callbackUrl:"/api/auth/callback/wechat",tokenEndpoint:"https://api.weixin.qq.com/sns/oauth2/access_token"},{id:"qq",name:"QQ \u767B\u5F55",region:"cn",envVars:["QQ_APP_ID","QQ_APP_KEY"],docs:"https://wiki.connect.qq.com",callbackUrl:"/api/auth/callback/qq",tokenEndpoint:"https://graph.qq.com/oauth2.0/token"},{id:"dingtalk",name:"\u9489\u9489",region:"cn",envVars:["DINGTALK_APP_ID","DINGTALK_APP_SECRET"],docs:"https://open.dingtalk.com/document/",callbackUrl:"/api/auth/callback/dingtalk",tokenEndpoint:"https://api.dingtalk.com/v1.0/oauth2/userAccessToken"},{id:"workweixin",name:"\u4F01\u4E1A\u5FAE\u4FE1",region:"cn",envVars:["WORKWEIXIN_CORP_ID","WORKWEIXIN_AGENT_ID","WORKWEIXIN_SECRET"],docs:"https://developer.work.weixin.qq.com",callbackUrl:"/api/auth/callback/workweixin",tokenEndpoint:"https://qyapi.weixin.qq.com/cgi-bin/gettoken"},{id:"feishu",name:"\u98DE\u4E66 / Lark",region:"cn",envVars:["FEISHU_APP_ID","FEISHU_APP_SECRET"],docs:"https://open.feishu.cn/document/",callbackUrl:"/api/auth/callback/feishu",tokenEndpoint:"https://open.feishu.cn/open-apis/authen/v1/access_token"},{id:"weibo",name:"\u5FAE\u535A",region:"cn",envVars:["WEIBO_APP_KEY","WEIBO_APP_SECRET"],docs:"https://open.weibo.com/wiki/",callbackUrl:"/api/auth/callback/weibo",tokenEndpoint:"https://api.weibo.com/oauth2/access_token"}],Yr=new Set(dt.map(e=>e.id));function In(e){return dt.find(t=>t.id===e)}function An(e){if(!e)return[];let t=e.split(",").map(r=>r.trim().toLowerCase()).filter(Boolean);if(t.includes("none"))return[];let o=[],n=new Set;for(let r of t)Yr.has(r)&&!n.has(r)&&(o.push(r),n.add(r));return o}import _e from"fs";import ve from"path";function qr(e,t){let o=`// apps/web/src/app/api/auth/callback/${e.id}/route.ts
|
|
360
360
|
// Generated by create-sailor \u2014 OAuth callback for ${e.name}
|
|
361
361
|
// Primary auth provider: ${t}
|
|
362
362
|
// Docs: ${e.docs}`,n={wechat:` const tokenRes = await fetch(
|
|
@@ -481,7 +481,7 @@ ${n[e.id]}
|
|
|
481
481
|
void state;
|
|
482
482
|
return NextResponse.redirect(new URL("/dashboard", req.url));
|
|
483
483
|
}
|
|
484
|
-
`}function
|
|
484
|
+
`}function Gr(e){return`"use client";
|
|
485
485
|
|
|
486
486
|
/**
|
|
487
487
|
* SocialLoginButtons \u2014 generated by create-sailor.
|
|
@@ -524,23 +524,23 @@ export function SocialLoginButtons() {
|
|
|
524
524
|
</div>
|
|
525
525
|
);
|
|
526
526
|
}
|
|
527
|
-
`}function
|
|
528
|
-
`+t):
|
|
527
|
+
`}function wn(e){_e.mkdirSync(e,{recursive:!0})}function Hr(e,t){let o=ve.join(e,".env.example");_e.existsSync(o)?_e.appendFileSync(o,`
|
|
528
|
+
`+t):_e.writeFileSync(o,t)}function Wr(e){let t=`# ${e.name} (${e.id}) \u2014 ${e.docs}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
529
529
|
`);return`${t}
|
|
530
530
|
${o}
|
|
531
|
-
`}async function
|
|
532
|
-
`);
|
|
533
|
-
`)}async function Rn(e,t,o){let n=Yt.join(e,"packages","cache");if(t==="none"){ae.existsSync(n)&&ae.rmSync(n,{recursive:!0,force:!0});return}let r=An(t);if(!r)throw new Error(`Unknown cache provider "${t}". Valid ids: ${Ct.map(i=>i.id).join(", ")}`);ae.existsSync(n)&&(Fr(e,r),Dr(e,"CACHE_PROVIDER",t))}var It=[{id:"turnstile",name:"Cloudflare Turnstile",region:"global",envVars:["NEXT_PUBLIC_TURNSTILE_SITE_KEY","TURNSTILE_SECRET_KEY"],docs:"https://developers.cloudflare.com/turnstile/",description:"Free, privacy-focused captcha alternative"},{id:"hcaptcha",name:"hCaptcha",region:"global",envVars:["NEXT_PUBLIC_HCAPTCHA_SITE_KEY","HCAPTCHA_SECRET_KEY"],docs:"https://docs.hcaptcha.com/",description:"Privacy-respecting captcha"},{id:"aliyun-slide",name:"\u963F\u91CC\u4E91\u9A8C\u8BC1\u7801 2.0",region:"cn",envVars:["ALIYUN_CAPTCHA_ACCESS_KEY_ID","ALIYUN_CAPTCHA_ACCESS_KEY_SECRET","ALIYUN_CAPTCHA_APP_KEY","NEXT_PUBLIC_ALIYUN_CAPTCHA_APP_KEY"],docs:"https://help.aliyun.com/product/28308.html",description:"\u963F\u91CC\u4E91\u667A\u80FD\u9A8C\u8BC1 (\u6ED1\u5757/\u65E0\u75D5)"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function kn(e){return It.find(t=>t.id===e)}var Ga=It.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});import te from"fs";import qt from"path";function H(e,t){let o=qt.join(e,".env.example");if(!te.existsSync(o)||t.envVars.length===0)return;let r=`# ${t.category??t.name}: ${t.name}`;if(te.readFileSync(o,"utf-8").includes(r))return;let c=["","# =============================================",r,t.docs?`# Docs: ${t.docs}`:null,"# =============================================",...t.envVars.map(a=>`${a}=""`),""].filter(a=>a!==null).join(`
|
|
534
|
-
`);te.appendFileSync(o,c)}function X(e,t,o){let n=qt.join(e,".env.example");if(!te.existsSync(n))return;let r=te.readFileSync(n,"utf-8"),i=`${t}="${o}"`,s=new RegExp(`^${t}=.*$`,"m");if(s.test(r)){te.writeFileSync(n,r.replace(s,i));return}let c=r.endsWith(`
|
|
531
|
+
`}async function Rn(e,t,o){if(!t||t.length===0)return;let n=ve.join(e,"apps","web");if(!_e.existsSync(n))return;let r=new Set,s=[];for(let l of t){if(r.has(l))continue;r.add(l);let m=In(l);m&&s.push(m)}if(s.length===0)return;for(let l of s)Hr(e,Wr(l));let i=ve.join(n,"src","app","api","auth","callback");for(let l of s){let m=ve.join(i,l.id);wn(m);let S=ve.join(m,"route.ts");_e.writeFileSync(S,qr(l,o))}let c=ve.join(n,"src","components","auth");wn(c);let a=ve.join(c,"SocialLoginButtons.tsx");_e.writeFileSync(a,Gr(s))}import et from"fs";import xn from"path";var Rt=[{id:"upstash-redis",name:"Upstash Redis",region:"both",envVars:["UPSTASH_REDIS_REST_URL","UPSTASH_REDIS_REST_TOKEN"],docs:"https://upstash.com/docs/redis",description:"Serverless Redis"},{id:"vercel-kv",name:"Vercel KV",region:"global",envVars:["KV_URL","KV_REST_API_URL","KV_REST_API_TOKEN","KV_REST_API_READ_ONLY_TOKEN"],docs:"https://vercel.com/docs/storage/vercel-kv",description:"Vercel native KV"},{id:"redis",name:"Redis (self-hosted)",region:"both",envVars:["REDIS_URL"],docs:"https://redis.io/docs/",description:"Self-hosted Redis"},{id:"dragonfly",name:"Dragonfly",region:"global",envVars:["DRAGONFLY_URL"],docs:"https://www.dragonflydb.io/docs",description:"Drop-in Redis replacement, faster"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:"Skip cache package"}];function kn(e){return Rt.find(t=>t.id===e)}var ic=Rt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});import J from"fs";import kt from"path";function Q(e,t){let o=kt.join(e,".env.example");if(!J.existsSync(o)||t.envVars.length===0)return;let r=`# ${t.category??t.name}: ${t.name}`;if(J.readFileSync(o,"utf-8").includes(r))return;let c=["","# =============================================",r,t.docs?`# Docs: ${t.docs}`:null,"# =============================================",...t.envVars.map(a=>`${a}=""`),""].filter(a=>a!==null).join(`
|
|
532
|
+
`);J.appendFileSync(o,c)}function Z(e,t,o){let n=kt.join(e,".env.example");if(!J.existsSync(n))return;let r=J.readFileSync(n,"utf-8"),s=`${t}="${o}"`,i=new RegExp(`^${t}=.*$`,"m");if(i.test(r)){J.writeFileSync(n,r.replace(i,s));return}let c=r.endsWith(`
|
|
535
533
|
`)?"":`
|
|
536
|
-
`;
|
|
537
|
-
`)}
|
|
538
|
-
`)
|
|
534
|
+
`;J.appendFileSync(n,`${c}${s}
|
|
535
|
+
`)}var Xr=["design","iam","commerce","integrations","platform","ops","ai"];function be(e,t){for(let n of Xr){let r=kt.join(e,"packages",n,t);if(J.existsSync(r))return r}let o=kt.join(e,"packages",t);return J.existsSync(o)?o:null}function H(e,t){let o=be(e,t);o&&J.rmSync(o,{recursive:!0,force:!0})}function Tn(e){return et.existsSync(e)?et.readFileSync(e,"utf-8"):null}function zr(e,t){let o=xn.join(e,".env.example"),n=Tn(o);if(n===null)return;let r=`# Cache: ${t.name}`;if(n.includes(r))return;let s=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
536
|
+
`);et.appendFileSync(o,s)}function Jr(e,t,o){let n=xn.join(e,".env.example"),r=Tn(n);if(r===null)return;let s=new RegExp(`^${t}=.*$`,"m");s.test(r)?et.writeFileSync(n,r.replace(s,`${t}="${o}"`)):et.appendFileSync(n,`${t}="${o}"
|
|
537
|
+
`)}async function Nn(e,t,o){let n=be(e,"cache");if(t==="none"){n&&et.rmSync(n,{recursive:!0,force:!0});return}let r=kn(t);if(!r)throw new Error(`Unknown cache provider "${t}". Valid ids: ${Rt.map(s=>s.id).join(", ")}`);n&&(zr(e,r),Jr(e,"CACHE_PROVIDER",t))}var xt=[{id:"turnstile",name:"Cloudflare Turnstile",region:"global",envVars:["NEXT_PUBLIC_TURNSTILE_SITE_KEY","TURNSTILE_SECRET_KEY"],docs:"https://developers.cloudflare.com/turnstile/",description:"Free, privacy-focused captcha alternative"},{id:"hcaptcha",name:"hCaptcha",region:"global",envVars:["NEXT_PUBLIC_HCAPTCHA_SITE_KEY","HCAPTCHA_SECRET_KEY"],docs:"https://docs.hcaptcha.com/",description:"Privacy-respecting captcha"},{id:"aliyun-slide",name:"\u963F\u91CC\u4E91\u9A8C\u8BC1\u7801 2.0",region:"cn",envVars:["ALIYUN_CAPTCHA_ACCESS_KEY_ID","ALIYUN_CAPTCHA_ACCESS_KEY_SECRET","ALIYUN_CAPTCHA_APP_KEY","NEXT_PUBLIC_ALIYUN_CAPTCHA_APP_KEY"],docs:"https://help.aliyun.com/product/28308.html",description:"\u963F\u91CC\u4E91\u667A\u80FD\u9A8C\u8BC1 (\u6ED1\u5757/\u65E0\u75D5)"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function On(e){return xt.find(t=>t.id===e)}var yc=xt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Un(e,t,o="global"){let n=On(t);if(!n)throw new Error(`Unknown captcha provider "${t}". Valid ids: ${xt.map(r=>r.id).join(", ")}`);if(n.id==="none"){H(e,"captcha");return}n.envVars.length>0&&Q(e,{category:"Captcha",name:n.name,envVars:n.envVars,docs:n.docs}),Z(e,"CAPTCHA_PROVIDER",n.id)}import Nt from"fs";import Qr from"path";var Tt=[{id:"sanity",name:"Sanity",region:"global",envVars:["SANITY_PROJECT_ID","SANITY_DATASET","SANITY_API_TOKEN","SANITY_API_VERSION"],docs:"https://www.sanity.io/docs",description:"Structured content platform with Studio"},{id:"contentful",name:"Contentful",region:"global",envVars:["CONTENTFUL_SPACE_ID","CONTENTFUL_ACCESS_TOKEN","CONTENTFUL_PREVIEW_ACCESS_TOKEN"],docs:"https://www.contentful.com/developers/docs/",description:"Headless CMS"},{id:"strapi",name:"Strapi",region:"both",envVars:["STRAPI_URL","STRAPI_API_TOKEN"],docs:"https://docs.strapi.io",description:"Open-source self-hosted CMS"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function Ln(e){return Tt.find(t=>t.id===e)}var _c=Tt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Mn(e,t,o="global"){let n=Ln(t);if(!n)throw new Error(`Unknown CMS provider "${t}". Valid ids: ${Tt.map(s=>s.id).join(", ")}`);let r=Qr.join(e,"apps","studio");if(n.id==="none"){H(e,"sanity"),Nt.existsSync(r)&&Nt.rmSync(r,{recursive:!0,force:!0});return}n.id!=="sanity"&&(H(e,"sanity"),Nt.existsSync(r)&&Nt.rmSync(r,{recursive:!0,force:!0})),n.envVars.length>0&&Q(e,{category:"CMS",name:n.name,envVars:n.envVars,docs:n.docs}),Z(e,"CMS_PROVIDER",n.id)}import k from"fs";import R from"path";var Zr=R.join("apps","web"),Jt=R.join("src","components","compliance"),ut=R.join("src","app","[locale]");function ee(e){return R.join(e,Zr)}function es(e){k.mkdirSync(e,{recursive:!0})}function W(e,t){es(R.dirname(e)),k.writeFileSync(e,t,"utf8")}function Qt(e,t){let o=R.join(e,".env.example");if(k.existsSync(o)){if(k.readFileSync(o,"utf8").includes(t.split(`
|
|
538
|
+
`)[0]??""))return;k.appendFileSync(o,`
|
|
539
539
|
`+t+`
|
|
540
|
-
`)}else
|
|
541
|
-
`)}var
|
|
540
|
+
`)}else k.writeFileSync(o,t+`
|
|
541
|
+
`)}var mt=`> **TEMPLATE \u2014 NOT LEGAL ADVICE.** This document is a boilerplate scaffold only.
|
|
542
542
|
> Consult a qualified lawyer in every jurisdiction you operate in before publishing.
|
|
543
|
-
> \u672C\u6587\u6863\u4E3A\u6A21\u677F\u811A\u624B\u67B6\uFF0C\u4E0D\u6784\u6210\u6CD5\u5F8B\u610F\u89C1\u3002\u53D1\u5E03\u524D\u8BF7\u54A8\u8BE2\u76F8\u5173\u53F8\u6CD5\u8F96\u533A\u7684\u5408\u683C\u5F8B\u5E08\u3002`,
|
|
543
|
+
> \u672C\u6587\u6863\u4E3A\u6A21\u677F\u811A\u624B\u67B6\uFF0C\u4E0D\u6784\u6210\u6CD5\u5F8B\u610F\u89C1\u3002\u53D1\u5E03\u524D\u8BF7\u54A8\u8BE2\u76F8\u5173\u53F8\u6CD5\u8F96\u533A\u7684\u5408\u683C\u5F8B\u5E08\u3002`,ts=`import Link from "next/link";
|
|
544
544
|
import { IcpFooter } from "@/components/compliance/IcpFooter";
|
|
545
545
|
import { PublicSecurityFooter } from "@/components/compliance/PublicSecurityFooter";
|
|
546
546
|
|
|
@@ -591,7 +591,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
591
591
|
</footer>
|
|
592
592
|
);
|
|
593
593
|
}
|
|
594
|
-
|
|
594
|
+
`,ns=`export function IcpFooter() {
|
|
595
595
|
const icp = process.env.NEXT_PUBLIC_ICP_BEIAN;
|
|
596
596
|
if (!icp) return null;
|
|
597
597
|
return (
|
|
@@ -605,7 +605,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
605
605
|
</a>
|
|
606
606
|
);
|
|
607
607
|
}
|
|
608
|
-
`,
|
|
608
|
+
`,os=`export function PublicSecurityFooter() {
|
|
609
609
|
const psb = process.env.NEXT_PUBLIC_PSB_BEIAN;
|
|
610
610
|
const psbCode = process.env.NEXT_PUBLIC_PSB_CODE;
|
|
611
611
|
if (!psb) return null;
|
|
@@ -622,7 +622,7 @@ export function Footer({ region = "global" }: FooterProps) {
|
|
|
622
622
|
</a>
|
|
623
623
|
);
|
|
624
624
|
}
|
|
625
|
-
`,
|
|
625
|
+
`,rs=`"use client";
|
|
626
626
|
|
|
627
627
|
import { useCallback, useEffect, useState } from "react";
|
|
628
628
|
|
|
@@ -849,7 +849,7 @@ function CategoryRow({ id, title, description, checked, disabled, onChange }: Ca
|
|
|
849
849
|
</label>
|
|
850
850
|
);
|
|
851
851
|
}
|
|
852
|
-
`,
|
|
852
|
+
`,ss=`interface AigcDisclaimerProps {
|
|
853
853
|
className?: string;
|
|
854
854
|
}
|
|
855
855
|
|
|
@@ -869,13 +869,13 @@ export function AigcDisclaimer({ className }: AigcDisclaimerProps) {
|
|
|
869
869
|
</div>
|
|
870
870
|
);
|
|
871
871
|
}
|
|
872
|
-
`,
|
|
872
|
+
`,is=`---
|
|
873
873
|
title: Privacy Policy / \u9690\u79C1\u653F\u7B56
|
|
874
874
|
description: How {{COMPANY_NAME}} collects, uses, shares, and protects personal information.
|
|
875
875
|
lastUpdated: 2026-01-01
|
|
876
876
|
---
|
|
877
877
|
|
|
878
|
-
${
|
|
878
|
+
${mt}
|
|
879
879
|
|
|
880
880
|
# Privacy Policy / \u9690\u79C1\u653F\u7B56
|
|
881
881
|
|
|
@@ -1014,13 +1014,13 @@ or in-app notification before the change takes effect.
|
|
|
1014
1014
|
**Data Controller / \u6570\u636E\u63A7\u5236\u8005:** {{COMPANY_NAME}}
|
|
1015
1015
|
**Address / \u5730\u5740:** {{COMPANY_ADDRESS}}
|
|
1016
1016
|
**Email / \u90AE\u7BB1:** {{COMPANY_CONTACT_EMAIL}}
|
|
1017
|
-
`,
|
|
1017
|
+
`,as=`---
|
|
1018
1018
|
title: Terms of Service / \u670D\u52A1\u6761\u6B3E
|
|
1019
1019
|
description: Terms governing your use of {{COMPANY_NAME}}.
|
|
1020
1020
|
lastUpdated: 2026-01-01
|
|
1021
1021
|
---
|
|
1022
1022
|
|
|
1023
|
-
${
|
|
1023
|
+
${mt}
|
|
1024
1024
|
|
|
1025
1025
|
# Terms of Service / \u670D\u52A1\u6761\u6B3E
|
|
1026
1026
|
|
|
@@ -1133,13 +1133,13 @@ conflict-of-laws principles. Disputes shall be resolved in the competent courts
|
|
|
1133
1133
|
## 11. Contact / \u8054\u7CFB\u65B9\u5F0F
|
|
1134
1134
|
|
|
1135
1135
|
{{COMPANY_NAME}} \xB7 {{COMPANY_ADDRESS}} \xB7 {{COMPANY_CONTACT_EMAIL}}
|
|
1136
|
-
`,
|
|
1136
|
+
`,cs=`---
|
|
1137
1137
|
title: AIGC Disclosure / \u751F\u6210\u5F0F AI \u670D\u52A1\u8BF4\u660E
|
|
1138
1138
|
description: Disclosure required by \u7F51\u4FE1\u529E\u300A\u751F\u6210\u5F0F\u4EBA\u5DE5\u667A\u80FD\u670D\u52A1\u7BA1\u7406\u6682\u884C\u529E\u6CD5\u300B
|
|
1139
1139
|
lastUpdated: 2026-01-01
|
|
1140
1140
|
---
|
|
1141
1141
|
|
|
1142
|
-
${
|
|
1142
|
+
${mt}
|
|
1143
1143
|
|
|
1144
1144
|
# AIGC Disclosure / \u751F\u6210\u5F0F AI \u670D\u52A1\u8BF4\u660E
|
|
1145
1145
|
|
|
@@ -1215,13 +1215,13 @@ human professional.
|
|
|
1215
1215
|
|
|
1216
1216
|
AI \u8F93\u51FA\u53EF\u80FD\u4E0D\u51C6\u786E\u3001\u5B58\u5728\u504F\u89C1\u6216\u8FC7\u65F6\u3002**\u8BF7\u52FF**\u5728\u672A\u7ECF\u5408\u683C\u4E13\u4E1A\u4EBA\u58EB\u72EC\u7ACB\u6838\u5B9E\u7684\u60C5\u51B5\u4E0B\uFF0C\u5C06 AI \u8F93\u51FA
|
|
1217
1217
|
\u7528\u4E8E\u533B\u7597\u3001\u6CD5\u5F8B\u3001\u91D1\u878D\u6216\u5B89\u5168\u6538\u5173\u7684\u51B3\u7B56\u3002
|
|
1218
|
-
`,
|
|
1218
|
+
`,ps=`---
|
|
1219
1219
|
title: Cross-Border Data Notice / \u6570\u636E\u51FA\u5883\u544A\u77E5
|
|
1220
1220
|
description: Notice of cross-border transfer of personal information, per PIPL.
|
|
1221
1221
|
lastUpdated: 2026-01-01
|
|
1222
1222
|
---
|
|
1223
1223
|
|
|
1224
|
-
${
|
|
1224
|
+
${mt}
|
|
1225
1225
|
|
|
1226
1226
|
# Cross-Border Data Notice / \u6570\u636E\u51FA\u5883\u544A\u77E5
|
|
1227
1227
|
|
|
@@ -1280,13 +1280,13 @@ Refusal may limit your ability to use features that rely on overseas services.
|
|
|
1280
1280
|
- \u4F20\u8F93\u4E2D\u52A0\u5BC6 (TLS 1.2+) \u53CA\u9759\u6001\u52A0\u5BC6 (AES-256)\u3002
|
|
1281
1281
|
- \u5408\u540C\u6761\u6B3E\u8981\u6C42\u5883\u5916\u63A5\u6536\u65B9\u63D0\u4F9B\u7B49\u540C\u4E8E PIPL \u7684\u4FDD\u62A4\u3002
|
|
1282
1282
|
- \u8BBF\u95EE\u63A7\u5236\u3001\u5BA1\u8BA1\u65E5\u5FD7\u548C\u8FDD\u89C4\u901A\u77E5\u7A0B\u5E8F\u3002
|
|
1283
|
-
`,
|
|
1283
|
+
`,ls=`---
|
|
1284
1284
|
title: Personal Information Collection List / \u4E2A\u4EBA\u4FE1\u606F\u6536\u96C6\u6E05\u5355
|
|
1285
1285
|
description: Itemized list of personal information collected, per PIPL Article 17.
|
|
1286
1286
|
lastUpdated: 2026-01-01
|
|
1287
1287
|
---
|
|
1288
1288
|
|
|
1289
|
-
${
|
|
1289
|
+
${mt}
|
|
1290
1290
|
|
|
1291
1291
|
# Personal Information Collection List / \u4E2A\u4EBA\u4FE1\u606F\u6536\u96C6\u6E05\u5355
|
|
1292
1292
|
|
|
@@ -1322,19 +1322,26 @@ To exercise your rights (access, rectify, delete, export, withdraw consent), con
|
|
|
1322
1322
|
**{{COMPANY_CONTACT_EMAIL}}**.
|
|
1323
1323
|
|
|
1324
1324
|
\u5982\u9700\u884C\u4F7F\u60A8\u7684\u6743\u5229\uFF08\u8BBF\u95EE\u3001\u66F4\u6B63\u3001\u5220\u9664\u3001\u5BFC\u51FA\u3001\u64A4\u56DE\u540C\u610F\uFF09\uFF0C\u8BF7\u8054\u7CFB **{{COMPANY_CONTACT_EMAIL}}**\u3002
|
|
1325
|
-
`;async function
|
|
1326
|
-
`))}async function
|
|
1327
|
-
`))}async function
|
|
1328
|
-
`))}async function
|
|
1329
|
-
`))}async function
|
|
1330
|
-
`)}import
|
|
1331
|
-
`+t):
|
|
1332
|
-
DATABASE_URL="${
|
|
1333
|
-
`),!
|
|
1334
|
-
|
|
1325
|
+
`;async function ds(e,t){let o=ee(e);if(!k.existsSync(o))return;let n=R.join(o,"src","components","compliance","Footer.tsx");W(n,ts);let r=R.join(o,"src","components","compliance","README.md");W(r,["# Compliance Components","",`Auto-generated by \`create-sailor\` for region: \`${t}\`.`,"","## Mounting the Footer","","```tsx",'import { Footer } from "@/components/compliance/Footer";','import { CookieBanner } from "@/components/compliance/CookieBanner";',"","export default function RootLayout({ children }) {"," return ("," <html>"," <body>"," {children}",` <Footer region="${t}" />`," <CookieBanner />"," </body>"," </html>"," );","}","```","","## Placeholders","","Policy MDX files contain `{{COMPANY_NAME}}`, `{{COMPANY_ADDRESS}}`,","`{{COMPANY_CONTACT_EMAIL}}`, `{{GOVERNING_JURISDICTION}}`, and","`{{AIGC_ALGORITHM_FILING_NUMBER}}`. These are filled by the `brand-apply` step.",""].join(`
|
|
1326
|
+
`))}async function us(e,t){let o=ee(e);if(!k.existsSync(o))return;let n=R.join(o,"src","components","compliance","CookieBanner.tsx");W(n,rs)}async function ms(e,t){let o=ee(e);if(!k.existsSync(o))return;let n=R.join(o,ut,"privacy","page.mdx");W(n,is)}async function gs(e,t){let o=ee(e);if(!k.existsSync(o))return;let n=R.join(o,ut,"terms","page.mdx");W(n,as)}async function fs(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,Jt,"IcpFooter.tsx");W(o,ns),Qt(e,["# Chinese ICP filing (\u5DE5\u4FE1\u90E8 ICP \u5907\u6848) \u2014 required to legally host a","# public-facing site from mainland China. Apply at https://beian.miit.gov.cn/",'NEXT_PUBLIC_ICP_BEIAN="" # \u5982 \u82CFICP\u59072024XXXXXX\u53F7'].join(`
|
|
1327
|
+
`))}async function ys(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,Jt,"PublicSecurityFooter.tsx");W(o,os),Qt(e,["# Chinese Public Security Bureau filing (\u516C\u5B89\u8054\u7F51\u5907\u6848)","# Required in addition to ICP. Apply at http://www.beian.gov.cn/",'NEXT_PUBLIC_PSB_BEIAN="" # \u5982 \u82CF\u516C\u7F51\u5B89\u5907 32011402XXXXXX\u53F7','NEXT_PUBLIC_PSB_CODE="" # 14-digit record code'].join(`
|
|
1328
|
+
`))}async function Ss(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,Jt,"AigcDisclaimer.tsx");W(o,ss),Qt(e,["# Generative AI algorithm filing (\u7B97\u6CD5\u5907\u6848\u53F7) \u2014 required by","# \u7F51\u4FE1\u529E\u300A\u751F\u6210\u5F0F\u4EBA\u5DE5\u667A\u80FD\u670D\u52A1\u7BA1\u7406\u6682\u884C\u529E\u6CD5\u300Bfor services offered in China.",'NEXT_PUBLIC_AIGC_FILING_NUMBER="" # e.g. \u7F51\u4FE1\u7B97\u5907XXXXXXXX\u53F7'].join(`
|
|
1329
|
+
`))}async function hs(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,ut,"aigc-disclosure","page.mdx");W(o,cs)}async function Es(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,ut,"cross-border-data","page.mdx");W(o,ps)}async function vs(e){let t=ee(e);if(!k.existsSync(t))return;let o=R.join(t,ut,"pii-collection","page.mdx");W(o,ls)}async function Dn(e,t){try{await ds(e,t),await us(e,t),await ms(e,t),await gs(e,t),(t==="cn"||t==="hybrid")&&(await fs(e),await ys(e),await Ss(e),await hs(e),await Es(e),await vs(e))}catch(o){throw console.error("applyComplianceTemplates failed:",o),new Error(`Failed to apply compliance templates for region "${t}". Check that ${e} is writable.`)}}import _s from"fs";import bs from"path";async function Fn(e,t){let o=bs.join(e,"nebutra.config.json");_s.writeFileSync(o,JSON.stringify(t,null,2)+`
|
|
1330
|
+
`)}import A from"fs";import Pe from"path";var Zt=[{id:"local",name:"Local / Self-hosted Docker",region:"both",supportedEngines:["postgresql","mysql","sqlite"],envVars:[{name:"DATABASE_URL",placeholder:"postgresql://postgres:postgres@localhost:5432/$PROJECT",comment:"Local Postgres via docker-compose (pnpm infra:up)"}],docs:"https://www.postgresql.org/docs/",description:"Docker on localhost. You operate it.",tier:"self-hosted"},{id:"supabase",name:"Supabase",region:"global",supportedEngines:["postgresql"],forcedEngine:"postgresql",keepDirectUrl:!0,envVars:[{name:"DATABASE_URL",placeholder:"postgresql://postgres.<project-ref>:<password>@aws-0-<region>.pooler.supabase.com:6543/postgres?pgbouncer=true",comment:"Supabase pooler \u2014 use for runtime queries"},{name:"DIRECT_URL",placeholder:"postgresql://postgres.<project-ref>:<password>@aws-0-<region>.pooler.supabase.com:5432/postgres",comment:"Supabase direct \u2014 use for Prisma migrations"},{name:"NEXT_PUBLIC_SUPABASE_URL",placeholder:"https://<project>.supabase.co"},{name:"NEXT_PUBLIC_SUPABASE_ANON_KEY",placeholder:""},{name:"SUPABASE_SERVICE_ROLE_KEY",placeholder:""}],docs:"https://supabase.com/docs",description:"Managed Postgres + auth/storage/realtime/edge-functions.",tier:"managed"},{id:"neon",name:"Neon",region:"global",supportedEngines:["postgresql"],forcedEngine:"postgresql",keepDirectUrl:!0,envVars:[{name:"DATABASE_URL",placeholder:"postgresql://<user>:<pwd>@ep-<branch>-pooler.<region>.neon.tech/<db>?sslmode=require",comment:"Neon pooled \u2014 runtime queries"},{name:"DIRECT_URL",placeholder:"postgresql://<user>:<pwd>@ep-<branch>.<region>.neon.tech/<db>?sslmode=require",comment:"Neon direct (no -pooler) \u2014 migrations"}],docs:"https://neon.tech/docs",description:"Serverless Postgres with branching and scale-to-zero.",tier:"managed"},{id:"vercel-postgres",name:"Vercel Postgres",region:"global",supportedEngines:["postgresql"],forcedEngine:"postgresql",keepDirectUrl:!0,envVars:[{name:"DATABASE_URL",placeholder:"$POSTGRES_PRISMA_URL",comment:"Vercel Marketplace injects POSTGRES_PRISMA_URL automatically"},{name:"DIRECT_URL",placeholder:"$POSTGRES_URL_NON_POOLING",comment:"Vercel Marketplace injects POSTGRES_URL_NON_POOLING automatically"}],docs:"https://vercel.com/docs/storage/vercel-postgres",description:"Neon-backed, auto-provisioned via Vercel Marketplace.",tier:"managed"},{id:"planetscale",name:"PlanetScale",region:"global",supportedEngines:["mysql"],forcedEngine:"mysql",prismaDatasourceExtras:['relationMode = "prisma"'],envVars:[{name:"DATABASE_URL",placeholder:"mysql://<user>:<pass>@<host>.psdb.cloud/<db>?sslaccept=strict",comment:'PlanetScale: relationMode = "prisma" (no FK support server-side)'}],docs:"https://planetscale.com/docs",description:"Managed MySQL with branching. No foreign keys.",tier:"managed"},{id:"railway",name:"Railway",region:"global",supportedEngines:["postgresql","mysql"],envVars:[{name:"DATABASE_URL",placeholder:"$DATABASE_URL",comment:"Railway service env var; injected when you link a Postgres service"}],docs:"https://docs.railway.app/databases/postgresql",description:"Generalist managed infra; standard TCP Postgres/MySQL.",tier:"managed"},{id:"aliyun-rds",name:"Aliyun RDS",region:"cn",supportedEngines:["postgresql","mysql"],envVars:[{name:"DATABASE_URL",placeholder:"postgresql://<user>:<pwd>@<rds-instance>.rds.aliyuncs.com:5432/<db>",comment:"Aliyun RDS Postgres \u2014 internal VPC endpoint recommended"}],docs:"https://www.alibabacloud.com/help/en/rds",description:"Aliyun RDS \u2014 China compliance + intranet routing.",tier:"managed"},{id:"tencent-cdb",name:"Tencent CloudDB (CDB)",region:"cn",supportedEngines:["mysql","postgresql"],envVars:[{name:"DATABASE_URL",placeholder:"mysql://<user>:<pwd>@<cdb-instance>.tencentcdb.com:3306/<db>",comment:"Tencent CloudDB \u2014 VPC endpoint recommended"}],docs:"https://intl.cloud.tencent.com/document/product/236",description:"Tencent CloudDB \u2014 China compliance.",tier:"managed"},{id:"none",name:"None",region:"both",supportedEngines:["postgresql","mysql","sqlite"],envVars:[],docs:"",description:"Skip database scaffolding.",tier:"self-hosted"}];function Ot(e){return Zt.find(t=>t.id===e)}function jn(e){return e==="cn"||e==="hybrid"?"local":"supabase"}function Ps(e){A.existsSync(e)&&A.rmSync(e,{recursive:!0,force:!0})}function Vn(e,t){return e.replace(/provider\s*=\s*"postgresql"/,`provider = "${t}"`)}function Cs(e){return e.replace(/^\s*directUrl\s*=\s*env\([^)]*\)\s*\n/gm,"")}function Kn(e){let t=e.replace(/^\s*extensions\s*=\s*\[[^\]]*\]\s*\n/gm,"");return t=t.replace(/previewFeatures\s*=\s*\[\s*"postgresqlExtensions"\s*\]\s*\n/g,""),t}function Is(e,t){let o=Pe.join(e,".env.example");A.existsSync(o)?A.appendFileSync(o,`
|
|
1331
|
+
`+t):A.writeFileSync(o,t)}async function Bn(e,t,o){let n=Pe.join(e,"packages","platform","db");if(t==="none"){Ps(n);return}let r=Pe.join(n,"prisma","schema.prisma"),s;if(t==="postgresql"?s=`postgresql://postgres:postgres@localhost:5432/${o}`:t==="mysql"?s=`mysql://root:root@localhost:3306/${o}`:s="file:./dev.db",Is(e,`# Database (${t})
|
|
1332
|
+
DATABASE_URL="${s}"
|
|
1333
|
+
`),!A.existsSync(r))return;let i=A.readFileSync(r,"utf8"),c=i;t==="mysql"?(c=Vn(c,"mysql"),c=Kn(c)):t==="sqlite"&&(c=Vn(c,"sqlite"),c=Cs(c),c=Kn(c)),c!==i&&A.writeFileSync(r,c)}function As(e){let t=Pe.join(e,".env.example");return A.existsSync(t)?A.readFileSync(t,"utf8"):""}function ws(e,t,o,n){let r=Pe.join(e,".env.example"),s=As(e),i=n?`# ${n}
|
|
1334
|
+
${t}="${o}"
|
|
1335
|
+
`:`${t}="${o}"
|
|
1336
|
+
`,c=new RegExp(`^\\s*${t}\\s*=.*$`,"m");if(c.test(s)){A.writeFileSync(r,s.replace(c,`${t}="${o}"`));return}s?A.appendFileSync(r,`
|
|
1337
|
+
${i}`):A.writeFileSync(r,i)}function Rs(e,t){let o=Pe.join(e,"packages","platform","db","prisma","schema.prisma");if(!A.existsSync(o))return;let n=A.readFileSync(o,"utf8"),r=/(datasource\s+db\s*\{[^}]*?)(\n})/;if(!n.match(r))return;let i=t.filter(l=>!n.includes(l.trim()));if(i.length===0)return;let c=i.map(l=>` ${l}`).join(`
|
|
1338
|
+
`),a=n.replace(r,`$1
|
|
1339
|
+
${c}$2`);A.writeFileSync(o,a)}function ks(e){let t=Pe.join(e,"packages","platform","db","prisma","schema.prisma");if(!A.existsSync(t))return;let o=A.readFileSync(t,"utf8");if(/directUrl\s*=\s*env\(/.test(o))return;let n=/(datasource\s+db\s*\{[\s\S]*?url\s*=\s*env\(\s*"DATABASE_URL"\s*\)[^\n]*\n)/;n.test(o)&&A.writeFileSync(t,o.replace(n,`$1 directUrl = env("DIRECT_URL")
|
|
1340
|
+
`))}async function $n(e,t,o){if(!(t.id==="none"||t.id==="local")){for(let n of t.envVars){let r=n.placeholder.replace(/\$PROJECT/g,o);ws(e,n.name,r,n.comment)}t.prismaDatasourceExtras&&t.prismaDatasourceExtras.length>0&&Rs(e,t.prismaDatasourceExtras),t.keepDirectUrl&&ks(e)}}import tt from"fs";import V from"path";import{fileURLToPath as xs}from"url";var Ts=xs(import.meta.url),Yn=V.dirname(Ts);function Ns(){let e=[V.join(Yn,"..","templates","deploy"),V.join(Yn,"..","..","templates","deploy")];for(let t of e)if(tt.existsSync(t))return t;return e[0]}async function qn(e,t){if(t==="none")return;let o=Ns();t==="vercel"?await tt.promises.copyFile(V.join(o,"vercel.json"),V.join(e,"vercel.json")):t==="railway"?await tt.promises.copyFile(V.join(o,"railway.toml"),V.join(e,"railway.toml")):t==="cloudflare"?await tt.promises.copyFile(V.join(o,"wrangler.toml"),V.join(e,"wrangler.toml")):t==="selfhost"&&(await tt.promises.copyFile(V.join(o,"docker-compose.yml"),V.join(e,"docker-compose.yml")),await tt.promises.copyFile(V.join(o,"Dockerfile.web"),V.join(e,"Dockerfile.web")))}import pe from"fs";import Ce from"path";import{fileURLToPath as Os}from"url";var Us=Os(import.meta.url),Gn=Ce.dirname(Us),Ls=/\.(md|mdx|json|ts|tsx|js|mjs|cjs|yaml|yml|css|html|vue)$/,Hn=["fumadocs","nextra","mintlify","docusaurus","vitepress"];function Ms(e){let t=[Ce.join(Gn,"..","templates","docs",e),Ce.join(Gn,"..","..","templates","docs",e)];for(let o of t)if(pe.existsSync(o))return o;return t[0]}async function Wn(e,t){if(t.framework==="none")return;if(!Hn.includes(t.framework))throw new Error(`Unsupported docs framework: "${t.framework}". Supported: ${Hn.join(", ")}, none.`);let o=Ms(t.framework),n=Ce.join(e,"apps","docs");if(!pe.existsSync(o))throw new Error(`Docs template not found: ${o}`);Xn(o,n),zn(n,{"{PRODUCT_NAME}":t.projectName})}function Xn(e,t){pe.mkdirSync(t,{recursive:!0});for(let o of pe.readdirSync(e,{withFileTypes:!0})){let n=Ce.join(e,o.name),r=Ce.join(t,o.name);o.isDirectory()?Xn(n,r):pe.copyFileSync(n,r)}}function zn(e,t){for(let o of pe.readdirSync(e,{withFileTypes:!0})){let n=Ce.join(e,o.name);if(o.isDirectory())zn(n,t);else if(Ls.test(o.name)){let r=pe.readFileSync(n,"utf-8");for(let[s,i]of Object.entries(t))r=r.split(s).join(i);pe.writeFileSync(n,r)}}}import Ae from"fs";import Ie from"path";var Ut=[{id:"resend",name:"Resend",region:"global",envVars:["RESEND_API_KEY","RESEND_FROM"],docs:"https://resend.com/docs",pkg:"resend",pkgVersion:"^4.0.1"},{id:"postmark",name:"Postmark",region:"global",envVars:["POSTMARK_API_KEY","POSTMARK_FROM"],docs:"https://postmarkapp.com/developer",pkg:"postmark",pkgVersion:"^4.0.5"},{id:"ses",name:"AWS SES",region:"global",envVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_SES_REGION","SES_FROM"],docs:"https://docs.aws.amazon.com/ses",pkg:"@aws-sdk/client-sesv2",pkgVersion:"^3.658.0"},{id:"aliyun-dm",name:"\u963F\u91CC\u4E91\u90AE\u4EF6\u63A8\u9001",region:"cn",envVars:["ALIYUN_DM_ACCESS_KEY","ALIYUN_DM_ACCESS_SECRET","ALIYUN_DM_ACCOUNT_NAME","ALIYUN_DM_FROM_ALIAS"],docs:"https://help.aliyun.com/product/29412.html",pkg:"@alicloud/dm20151123",pkgVersion:"^3.0.0"},{id:"tencent-ses",name:"\u817E\u8BAF\u4E91 SES",region:"cn",envVars:["TENCENT_SECRET_ID","TENCENT_SECRET_KEY","TENCENT_SES_FROM_EMAIL"],docs:"https://cloud.tencent.com/document/product/1288",pkg:"tencentcloud-sdk-nodejs-ses",pkgVersion:"^4.0.900"},{id:"netease",name:"\u7F51\u6613\u4F01\u4E1A\u90AE\u7BB1",region:"cn",envVars:["NETEASE_USER","NETEASE_PASS","NETEASE_FROM"],docs:"https://qiye.163.com/help",pkg:"nodemailer",pkgVersion:"^6.9.15"},{id:"none",name:"None",region:"global",envVars:[],docs:""}];function Jn(e){return Ut.find(t=>t.id===e)}var Hc=Ut.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});var we="@nebutra/email";function en(e,t){Ae.mkdirSync(Ie.dirname(e),{recursive:!0}),Ae.writeFileSync(e,t)}function Ds(e,t){let o=Ie.join(e,".env.example");Ae.existsSync(o)?Ae.appendFileSync(o,`
|
|
1341
|
+
`+t):Ae.writeFileSync(o,t)}function Fs(e){if(e.envVars.length===0)return"";let t=`# Email provider: ${e.name}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
1335
1342
|
`);return`${t}
|
|
1336
1343
|
${o}
|
|
1337
|
-
`}var
|
|
1344
|
+
`}var js=`/**
|
|
1338
1345
|
* Public inputs accepted by the \`sendEmail\` wrapper.
|
|
1339
1346
|
* Keep the surface minimal \u2014 individual providers may accept more, but
|
|
1340
1347
|
* consumers should code against this shape for portability.
|
|
@@ -1354,8 +1361,8 @@ export interface SendEmailResult {
|
|
|
1354
1361
|
/** Raw provider response \u2014 useful for debugging, not a stable contract. */
|
|
1355
1362
|
raw?: unknown;
|
|
1356
1363
|
}
|
|
1357
|
-
`;function
|
|
1358
|
-
* @${
|
|
1364
|
+
`;function Vs(){return`/**
|
|
1365
|
+
* @${we.slice(1)} \u2014 Resend transactional email wrapper.
|
|
1359
1366
|
*
|
|
1360
1367
|
* Docs: https://resend.com/docs
|
|
1361
1368
|
*/
|
|
@@ -1393,8 +1400,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1393
1400
|
}
|
|
1394
1401
|
|
|
1395
1402
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1396
|
-
`}function
|
|
1397
|
-
* @${
|
|
1403
|
+
`}function Ks(){return`/**
|
|
1404
|
+
* @${we.slice(1)} \u2014 Postmark transactional email wrapper.
|
|
1398
1405
|
*
|
|
1399
1406
|
* Docs: https://postmarkapp.com/developer
|
|
1400
1407
|
*/
|
|
@@ -1431,8 +1438,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1431
1438
|
}
|
|
1432
1439
|
|
|
1433
1440
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1434
|
-
`}function
|
|
1435
|
-
* @${
|
|
1441
|
+
`}function Bs(){return`/**
|
|
1442
|
+
* @${we.slice(1)} \u2014 AWS SES v2 transactional email wrapper.
|
|
1436
1443
|
*
|
|
1437
1444
|
* Docs: https://docs.aws.amazon.com/ses
|
|
1438
1445
|
*/
|
|
@@ -1487,8 +1494,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1487
1494
|
}
|
|
1488
1495
|
|
|
1489
1496
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1490
|
-
`}function
|
|
1491
|
-
* @${
|
|
1497
|
+
`}function $s(){return`/**
|
|
1498
|
+
* @${we.slice(1)} \u2014 \u963F\u91CC\u4E91\u90AE\u4EF6\u63A8\u9001 (Aliyun DirectMail) wrapper.
|
|
1492
1499
|
*
|
|
1493
1500
|
* Docs: https://help.aliyun.com/product/29412.html
|
|
1494
1501
|
*/
|
|
@@ -1537,8 +1544,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1537
1544
|
}
|
|
1538
1545
|
|
|
1539
1546
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1540
|
-
`}function
|
|
1541
|
-
* @${
|
|
1547
|
+
`}function Ys(){return`/**
|
|
1548
|
+
* @${we.slice(1)} \u2014 \u817E\u8BAF\u4E91 SES wrapper.
|
|
1542
1549
|
*
|
|
1543
1550
|
* Docs: https://cloud.tencent.com/document/product/1288
|
|
1544
1551
|
*/
|
|
@@ -1585,8 +1592,8 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1585
1592
|
}
|
|
1586
1593
|
|
|
1587
1594
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1588
|
-
`}function
|
|
1589
|
-
* @${
|
|
1595
|
+
`}function qs(){return`/**
|
|
1596
|
+
* @${we.slice(1)} \u2014 \u7F51\u6613\u4F01\u4E1A\u90AE\u7BB1 via SMTP (nodemailer).
|
|
1590
1597
|
*
|
|
1591
1598
|
* Docs: https://qiye.163.com/help
|
|
1592
1599
|
*/
|
|
@@ -1623,24 +1630,24 @@ export async function sendEmail(input: SendEmailInput): Promise<SendEmailResult>
|
|
|
1623
1630
|
}
|
|
1624
1631
|
|
|
1625
1632
|
export type { SendEmailInput, SendEmailResult } from "./types";
|
|
1626
|
-
`}function
|
|
1627
|
-
`}}function
|
|
1628
|
-
`}async function
|
|
1633
|
+
`}function Gs(e){switch(e){case"resend":return Vs();case"postmark":return Ks();case"ses":return Bs();case"aliyun-dm":return $s();case"tencent-ses":return Ys();case"netease":return qs();case"none":return`export {};
|
|
1634
|
+
`}}function Hs(e){let t={};e.pkg&&e.pkgVersion&&(t[e.pkg]=e.pkgVersion),e.id==="aliyun-dm"&&(t["@alicloud/openapi-client"]="^0.4.12");let o={name:we,version:"0.1.0",private:!0,description:`Transactional email via ${e.name}`,type:"module",main:"./dist/index.js",exports:{".":{import:"./dist/index.js",types:"./dist/index.d.ts"}},scripts:{build:"tsup",typecheck:"tsc --noEmit",clean:"rm -rf dist .turbo"},dependencies:t,devDependencies:{tsup:"^8.5.0",typescript:"catalog:"}};return JSON.stringify(o,null,2)+`
|
|
1635
|
+
`}async function Qn(e,t,o){if(t==="none")return;let n=Jn(t);if(!n)throw new Error(`Unknown email provider "${t}". Valid ids: ${Ut.map(i=>i.id).join(", ")}`);let r=Ie.join(e,"packages","email"),s=Ie.join(r,"src");Ae.existsSync(s)&&Ae.rmSync(s,{recursive:!0,force:!0}),en(Ie.join(r,"package.json"),Hs(n)),en(Ie.join(s,"types.ts"),js),en(Ie.join(s,"index.ts"),Gs(n.id)),Ds(e,Fs(n))}import Lt from"fs";import Zn from"path";async function eo(e,t){let o=`# Automatically injected by create-sailor
|
|
1629
1636
|
DATABASE_URL="${String(t.databaseUrl)}"
|
|
1630
1637
|
|
|
1631
1638
|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="${String(t.clerkPublishable)}"
|
|
1632
1639
|
CLERK_SECRET_KEY="${String(t.clerkSecret)}"
|
|
1633
1640
|
|
|
1634
1641
|
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
|
|
1635
|
-
`,n=
|
|
1636
|
-
`+o)}import
|
|
1642
|
+
`,n=Zn.join(e,".env"),r=Zn.join(e,".env.local");!Lt.existsSync(n)&&!Lt.existsSync(r)?Lt.writeFileSync(r,o):Lt.appendFileSync(r,`
|
|
1643
|
+
`+o)}import no from"crypto";import gt from"fs";import to from"path";function Re(e){return no.randomBytes(e).toString("base64").replace(/=+$/,"")}function Ws(e){return no.randomBytes(e).toString("hex")}function Xs(){return{AUTH_SECRET:Re(32),NEXTAUTH_SECRET:Re(32),BETTER_AUTH_SECRET:Re(32),JWT_SECRET:Re(32),WEBHOOK_SECRET:Ws(16),ENCRYPTION_KEY:Re(32),API_SECRET_KEY:Re(32),SESSION_SECRET:Re(32)}}function zs(e){return e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}function Js(e,t,o){let n=new RegExp(`^(?!#)\\s*${t}=.*$`,"gm"),r=zs(o);return e.replace(n,`${t}="${r}"`)}async function oo(e){let t=to.join(e,".env.example"),o=to.join(e,".env.local");if(!gt.existsSync(t))return null;try{let n=gt.readFileSync(t,"utf8"),r=Xs(),i=gt.existsSync(o)?gt.readFileSync(o,"utf8"):n;for(let c of Object.keys(r))i=Js(i,c,r[c]);return gt.writeFileSync(o,i),r}catch(n){let r=n instanceof Error?n.message:String(n);throw console.error(`[create-sailor] Failed to generate env secrets: ${r}`),new Error(`Unable to write .env.local: ${r}`)}}var Mt=[{id:"vercel-flags",name:"Vercel Flags SDK",region:"global",envVars:["FLAGS_SECRET"],docs:"https://vercel.com/docs/workflow-collaboration/feature-flags",description:"Native Vercel feature flags"},{id:"growthbook",name:"GrowthBook",region:"both",envVars:["GROWTHBOOK_API_HOST","GROWTHBOOK_CLIENT_KEY","GROWTHBOOK_DECRYPTION_KEY"],docs:"https://docs.growthbook.io",description:"Open-source A/B testing + flags"},{id:"configcat",name:"ConfigCat",region:"global",envVars:["CONFIGCAT_SDK_KEY"],docs:"https://configcat.com/docs/",description:"Feature flag & config management"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function ro(e){return Mt.find(t=>t.id===e)}var ip=Mt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function so(e,t,o="global"){let n=ro(t);if(!n)throw new Error(`Unknown feature-flags provider "${t}". Valid ids: ${Mt.map(r=>r.id).join(", ")}`);if(n.id==="none"){H(e,"feature-flags");return}n.envVars.length>0&&Q(e,{category:"Feature Flags",name:n.name,envVars:n.envVars,docs:n.docs}),Z(e,"FEATURE_FLAGS_PROVIDER",n.id)}import{execFileSync as co}from"child_process";import L from"fs";import Qs from"os";import le from"path";import Zs from"ignore";var ei={mirror:{repo:process.env.SAILOR_TEMPLATE_REPO||"Nebutra/Sailor-Template",ref:process.env.SAILOR_TEMPLATE_REF||"main",applyIgnore:!1,allowMutableFallback:process.env.SAILOR_TEMPLATE_ALLOW_MUTABLE_FALLBACK==="1"},main:{repo:"Nebutra/Nebutra-Sailor",ref:"main",applyIgnore:!0,allowMutableFallback:process.env.SAILOR_TEMPLATE_ALLOW_MUTABLE_FALLBACK==="1"}};function ti(e,t){return`https://github.com/${e}/archive/refs/heads/${t}.tar.gz`}function ni(e,t){return`https://github.com/${e}/archive/${t}.tar.gz`}function po(e){return/^[0-9a-f]{7,40}$/i.test(e)}function oi(e,t){let o=`https://github.com/${e}.git`,n=[`refs/heads/${t}`,`refs/tags/${t}`];for(let r of n)try{let s=co("git",["ls-remote",o,r],{encoding:"utf8",stdio:["ignore","pipe","ignore"]}).trim();if(!s)continue;let[i]=s.split(/\s+/);if(po(i))return i}catch{}return null}async function ri(e,t){if(po(t))return t;let o=await fetch(`https://api.github.com/repos/${e}/commits/${encodeURIComponent(t)}`,{headers:{Accept:"application/vnd.github+json","User-Agent":"create-sailor"}});if(!o.ok){let r=oi(e,t);if(r)return r;throw new Error(`Failed to resolve ${e}@${t} (GitHub API ${o.status})`)}let n=await o.json();if(typeof n.sha!="string"||n.sha.length===0)throw new Error(`GitHub API did not return a commit SHA for ${e}@${t}`);return n.sha}function lo(e,t,o){let n=L.readdirSync(t,{withFileTypes:!0});for(let r of n){if(r.name==="node_modules"||r.name===".git")continue;let s=le.join(t,r.name),i=le.relative(e,s).split(le.sep).join("/");r.isDirectory()?(o.push(i+"/"),lo(e,s,o)):o.push(i)}}function si(e){let t=le.join(e,".templateignore");if(!L.existsSync(t))return;let o=L.readFileSync(t,"utf8"),n=Zs().add(o),r=[];lo(e,e,r);let s=r.map(a=>a.endsWith("/")?a.slice(0,-1):a),i=new Set(n.filter(s)),c=s.filter(a=>!i.has(a)).sort((a,l)=>l.split("/").length-a.split("/").length);for(let a of c){let l=le.join(e,a);try{L.existsSync(l)&&L.rmSync(l,{recursive:!0,force:!0})}catch{}}try{L.existsSync(t)&&L.rmSync(t,{force:!0})}catch{}}async function io(e,t){let o=await fetch(e,{headers:{Accept:"application/octet-stream","User-Agent":"create-sailor"}});if(!o.ok)throw new Error(`Failed to download archive (${o.status})`);let n=Buffer.from(await o.arrayBuffer());if(n.byteLength<1024)throw new Error(`Downloaded archive is unexpectedly small (${n.byteLength} bytes)`);L.writeFileSync(t,n);let r=L.statSync(t);if(r.size!==n.byteLength)throw new Error(`Archive write verification failed (${r.size} != ${n.byteLength})`);return r.size}function ao(e,t){co("tar",["-xzf",e,"-C",t,"--strip-components=1"],{stdio:"ignore"})}function uo(e){try{let t=L.readdirSync(e);for(let o of t)L.rmSync(le.join(e,o),{recursive:!0,force:!0})}catch{}}async function ii(e,t){let o=L.mkdtempSync(le.join(Qs.tmpdir(),"create-sailor-")),n=le.join(o,"template.tar.gz");try{let r=await ri(e.repo,e.ref);await io(ni(e.repo,r),n),ao(n,t)}catch(r){if(!e.allowMutableFallback)throw r;uo(t),await io(ti(e.repo,e.ref),n),ao(n,t)}finally{L.rmSync(o,{recursive:!0,force:!0})}}async function mo(e){L.mkdirSync(e,{recursive:!0});let o=process.env.SAILOR_TEMPLATE_SOURCE==="main"?["main"]:["mirror","main"],n;for(let r of o){let s=ei[r];try{if(await ii(s,e),s.applyIgnore)try{si(e)}catch(i){throw new Error(`Template cloned, but .templateignore processing failed: ${i instanceof Error?i.message:String(i)}`)}return}catch(i){n=i instanceof Error?i:new Error(String(i)),uo(e)}}throw new Error(`Failed to download the template from any source. Last error: ${n?.message??"unknown"}
|
|
1637
1644
|
|
|
1638
1645
|
Ensure you have internet access and 'tar' installed.
|
|
1639
|
-
You can override the source with SAILOR_TEMPLATE_REPO / SAILOR_TEMPLATE_REF env vars.`)}import
|
|
1640
|
-
`);
|
|
1641
|
-
`);
|
|
1642
|
-
`)}function
|
|
1643
|
-
`+t):
|
|
1646
|
+
You can override the source with SAILOR_TEMPLATE_REPO / SAILOR_TEMPLATE_REF env vars.`)}import ke from"fs";import tn from"path";async function go(e,t){let o=tn.join(e,"packages","mcp");if(t==="off"){ke.existsSync(o)&&ke.rmSync(o,{recursive:!0,force:!0});let r=tn.join(e,"packages","cli","src","commands","mcp-server.ts");ke.existsSync(r)&&ke.rmSync(r,{force:!0});return}let n=tn.join(e,".env.example");if(ke.existsSync(n)){let r=ke.readFileSync(n,"utf-8"),s="# MCP Server (Agent-friendly API)";if(!r.includes(s)){let i=["","# =============================================",s,"# Docs: https://modelcontextprotocol.io","# =============================================","# MCP server exposes your SaaS capabilities to AI agents via MCP protocol.","# Run: pnpm mcp-server (starts stdio server)","# No config required for stdio transport. HTTP transport needs:",'MCP_HTTP_PORT="3100"','MCP_API_KEY=""',""].join(`
|
|
1647
|
+
`);ke.appendFileSync(n,i)}}}import ft from"fs";import fo from"path";async function yo(e,t,o){let n=fo.join(e,"packages","metering"),r;if(t==="auto"?r=o!=="none"?"on":"off":t==="on"||t==="off"?r=t:r="off",r==="off"){ft.existsSync(n)&&ft.rmSync(n,{recursive:!0,force:!0});return}let s=fo.join(e,".env.example");if(!ft.existsSync(s))return;let i=ft.readFileSync(s,"utf-8"),c="# Metering (usage-based billing)";if(!i.includes(c)){let a=["","# =============================================",c,"# Docs: see packages/commerce/metering/README.md","# =============================================","# ClickHouse-based metering pipeline for consumption billing.",'CLICKHOUSE_URL=""','CLICKHOUSE_USER="default"','CLICKHOUSE_PASSWORD=""','CLICKHOUSE_DATABASE="metering"',""].join(`
|
|
1648
|
+
`);ft.appendFileSync(s,a)}}import re from"fs";import M from"path";var So=[{id:"sentry",name:"Sentry",region:"both",envVars:["SENTRY_DSN","NEXT_PUBLIC_SENTRY_DSN"],docs:"https://docs.sentry.io",pkg:"@sentry/nextjs",instrumentationFile:"apps/web/instrumentation.ts"},{id:"datadog",name:"Datadog",region:"global",envVars:["DD_API_KEY","DD_APP_KEY","DD_SITE"],docs:"https://docs.datadoghq.com",pkg:"dd-trace",instrumentationFile:"apps/web/instrumentation.ts"},{id:"bugsnag",name:"Bugsnag",region:"global",envVars:["BUGSNAG_API_KEY","NEXT_PUBLIC_BUGSNAG_API_KEY"],docs:"https://docs.bugsnag.com",pkg:"@bugsnag/js",instrumentationFile:"apps/web/src/lib/monitoring/bugsnag.ts"},{id:"aliyun-arms",name:"\u963F\u91CC\u4E91 ARMS",region:"cn",envVars:["ALIYUN_ARMS_LICENSE_KEY","ALIYUN_ARMS_APP_NAME","NEXT_PUBLIC_ARMS_PID"],docs:"https://help.aliyun.com/product/34364.html",instrumentationFile:"apps/web/src/components/monitoring/ArmsScript.tsx"},{id:"tingyun",name:"\u542C\u4E91 TingYun",region:"cn",envVars:["TINGYUN_LICENSE_KEY","NEXT_PUBLIC_TINGYUN_APP_KEY"],docs:"https://www.tingyun.com/docs",instrumentationFile:"apps/web/src/components/monitoring/TingYunScript.tsx"},{id:"none",name:"None",region:"both",envVars:[],docs:""}],Pp=So.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function ho(e){return So.find(t=>t.id===e)}var nn={sentry:{"@sentry/nextjs":"^8.45.0"},datadog:{"dd-trace":"^5.32.0","@datadog/browser-rum":"^5.30.0"},bugsnag:{"@bugsnag/js":"^8.2.0","@bugsnag/plugin-react":"^8.2.0"}};function ai(e){re.mkdirSync(e,{recursive:!0})}function on(e,t){let o=M.join(e,"package.json");if(!re.existsSync(o))return;let n;try{n=JSON.parse(re.readFileSync(o,"utf-8"))}catch(c){console.error(`Failed to parse ${o}:`,c);return}let s={...n.dependencies??{}};for(let[c,a]of Object.entries(t))s[c]||(s[c]=a);let i={...n,dependencies:s};re.writeFileSync(o,JSON.stringify(i,null,2)+`
|
|
1649
|
+
`)}function te(e,t){ai(M.dirname(e)),re.writeFileSync(e,t)}function yt(e,t){let o=M.join(e,".env.example");re.existsSync(o)?re.appendFileSync(o,`
|
|
1650
|
+
`+t):re.writeFileSync(o,t)}function ci(e){let t=`import * as Sentry from "@sentry/nextjs";
|
|
1644
1651
|
|
|
1645
1652
|
Sentry.init({
|
|
1646
1653
|
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
|
@@ -1683,14 +1690,14 @@ export async function register() {
|
|
|
1683
1690
|
}
|
|
1684
1691
|
|
|
1685
1692
|
export { captureRequestError as onRequestError } from "@sentry/nextjs";
|
|
1686
|
-
`;
|
|
1693
|
+
`;te(M.join(e,"sentry.client.config.ts"),t),te(M.join(e,"sentry.server.config.ts"),o),te(M.join(e,"sentry.edge.config.ts"),n),te(M.join(e,"instrumentation.ts"),r)}function pi(){return`# Sentry \u2014 error tracking + performance monitoring
|
|
1687
1694
|
# Get your DSN from https://sentry.io/settings/projects/<project>/keys/
|
|
1688
1695
|
SENTRY_DSN=
|
|
1689
1696
|
NEXT_PUBLIC_SENTRY_DSN=
|
|
1690
1697
|
SENTRY_ORG=
|
|
1691
1698
|
SENTRY_PROJECT=
|
|
1692
1699
|
SENTRY_AUTH_TOKEN=
|
|
1693
|
-
`}function
|
|
1700
|
+
`}function li(e){let t=`/**
|
|
1694
1701
|
* Next.js instrumentation hook \u2014 wires Datadog APM (dd-trace) into Node runtime.
|
|
1695
1702
|
* Edge runtime is not supported by dd-trace; only nodejs is initialized.
|
|
1696
1703
|
*/
|
|
@@ -1738,7 +1745,7 @@ export function initDatadogRum(): void {
|
|
|
1738
1745
|
});
|
|
1739
1746
|
initialized = true;
|
|
1740
1747
|
}
|
|
1741
|
-
`;
|
|
1748
|
+
`;te(M.join(e,"instrumentation.ts"),t),te(M.join(e,"src","lib","monitoring","datadog-rum.ts"),o)}function di(){return`# Datadog \u2014 APM + RUM
|
|
1742
1749
|
# Server APM (dd-trace)
|
|
1743
1750
|
DD_API_KEY=
|
|
1744
1751
|
DD_APP_KEY=
|
|
@@ -1753,7 +1760,7 @@ NEXT_PUBLIC_DD_SITE=datadoghq.com
|
|
|
1753
1760
|
NEXT_PUBLIC_DD_SERVICE=sailor-web
|
|
1754
1761
|
NEXT_PUBLIC_DD_ENV=production
|
|
1755
1762
|
NEXT_PUBLIC_DD_VERSION=
|
|
1756
|
-
`}function
|
|
1763
|
+
`}function ui(e){let t=`/**
|
|
1757
1764
|
* Bugsnag server-side initialization.
|
|
1758
1765
|
* Import this once at the top of your server entrypoint or a shared init module.
|
|
1759
1766
|
*/
|
|
@@ -1802,11 +1809,11 @@ export function getBugsnagErrorBoundary() {
|
|
|
1802
1809
|
errorBoundary = plugin.createErrorBoundary(React) as typeof errorBoundary;
|
|
1803
1810
|
return errorBoundary;
|
|
1804
1811
|
}
|
|
1805
|
-
`;
|
|
1812
|
+
`;te(M.join(e,"src","lib","monitoring","bugsnag.server.ts"),t),te(M.join(e,"src","lib","monitoring","bugsnag.client.tsx"),o)}function mi(){return`# Bugsnag \u2014 error tracking
|
|
1806
1813
|
BUGSNAG_API_KEY=
|
|
1807
1814
|
NEXT_PUBLIC_BUGSNAG_API_KEY=
|
|
1808
1815
|
APP_VERSION=
|
|
1809
|
-
`}function
|
|
1816
|
+
`}function gi(e){te(M.join(e,"src","components","monitoring","ArmsScript.tsx"),`"use client";
|
|
1810
1817
|
/**
|
|
1811
1818
|
* \u963F\u91CC\u4E91 ARMS (Application Real-time Monitoring Service) browser agent.
|
|
1812
1819
|
* Injects the official JS agent via next/script.
|
|
@@ -1827,11 +1834,11 @@ export function ArmsScript() {
|
|
|
1827
1834
|
/>
|
|
1828
1835
|
);
|
|
1829
1836
|
}
|
|
1830
|
-
`)}function
|
|
1837
|
+
`)}function fi(){return`# \u963F\u91CC\u4E91 ARMS \u2014 APM (\u4E2D\u56FD\u533A)
|
|
1831
1838
|
ALIYUN_ARMS_LICENSE_KEY=
|
|
1832
1839
|
ALIYUN_ARMS_APP_NAME=
|
|
1833
1840
|
NEXT_PUBLIC_ARMS_PID=
|
|
1834
|
-
`}function
|
|
1841
|
+
`}function yi(e){te(M.join(e,"src","components","monitoring","TingYunScript.tsx"),`"use client";
|
|
1835
1842
|
/**
|
|
1836
1843
|
* \u542C\u4E91 Browser Agent (\u524D\u7AEF\u6027\u80FD\u76D1\u63A7).
|
|
1837
1844
|
* Docs: https://www.tingyun.com/docs
|
|
@@ -1849,12 +1856,12 @@ export function TingYunScript() {
|
|
|
1849
1856
|
/>
|
|
1850
1857
|
);
|
|
1851
1858
|
}
|
|
1852
|
-
`)}function
|
|
1859
|
+
`)}function Si(){return`# \u542C\u4E91 TingYun \u2014 APM (\u4E2D\u56FD\u533A)
|
|
1853
1860
|
TINGYUN_LICENSE_KEY=
|
|
1854
1861
|
NEXT_PUBLIC_TINGYUN_APP_KEY=
|
|
1855
|
-
`}async function
|
|
1856
|
-
`)}}import
|
|
1857
|
-
`+t):
|
|
1862
|
+
`}async function Eo(e,t,o="global"){let n=ho(t);if(!n||n.id==="none")return;let r=M.join(e,"apps","web");if(!re.existsSync(r))return;let s=n.id;try{switch(s){case"sentry":ci(r),on(r,nn.sentry),yt(e,pi());return;case"datadog":li(r),on(r,nn.datadog),yt(e,di());return;case"bugsnag":ui(r),on(r,nn.bugsnag),yt(e,mi());return;case"aliyun-arms":gi(r),yt(e,fi());return;case"tingyun":yi(r),yt(e,Si());return;default:return}}catch(i){throw console.error(`Failed to apply monitoring selection "${t}":`,i),new Error(`Monitoring scaffold for "${t}" failed \u2014 see stderr for details.`)}}var Dt=[{id:"novu",name:"Novu",region:"both",envVars:["NOVU_API_KEY","NOVU_APPLICATION_IDENTIFIER"],docs:"https://docs.novu.co",description:"Multi-channel notification infrastructure"},{id:"knock",name:"Knock",region:"global",envVars:["KNOCK_API_KEY","KNOCK_SIGNING_KEY","KNOCK_PUBLIC_API_KEY"],docs:"https://docs.knock.app",description:"Product notifications platform"},{id:"custom",name:"Custom (direct dispatchers)",region:"both",envVars:[],docs:"",description:"Use email + sms + push directly"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function vo(e){return Dt.find(t=>t.id===e)}var kp=Dt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function _o(e,t,o="global"){let n=vo(t);if(!n)throw new Error(`Unknown notifications provider "${t}". Valid ids: ${Dt.map(r=>r.id).join(", ")}`);if(n.id==="none"){H(e,"notifications");return}n.envVars.length>0&&Q(e,{category:"Notifications",name:n.name,envVars:n.envVars,docs:n.docs}),Z(e,"NOTIFICATIONS_PROVIDER",n.id)}import rn from"fs";import hi from"path";async function bo(e,t){let o=hi.join(e,"package.json");if(rn.existsSync(o)){let n=rn.readFileSync(o,"utf8"),r=JSON.parse(n);r.name=t,r.version="0.1.0",rn.writeFileSync(o,JSON.stringify(r,null,2)+`
|
|
1863
|
+
`)}}import Ft from"fs";import nt from"path";import{fileURLToPath as Ei}from"url";var vi=Ei(import.meta.url),Po=nt.dirname(vi);function _i(){let e=[nt.join(Po,"..","templates","orm","drizzle"),nt.join(Po,"..","..","templates","orm","drizzle")];for(let t of e)if(Ft.existsSync(t))return t;return null}function Co(e,t){Ft.mkdirSync(t,{recursive:!0});for(let o of Ft.readdirSync(e,{withFileTypes:!0})){let n=nt.join(e,o.name),r=nt.join(t,o.name);o.isDirectory()?Co(n,r):Ft.copyFileSync(n,r)}}async function Io(e,t,o){if(t!=="drizzle")return{applied:!1};if(o!=="postgresql")return{applied:!1,reason:`Drizzle template currently targets Postgres only; --db=${o} keeps Prisma as sole ORM`};let n=_i();if(!n)return{applied:!1,reason:"Drizzle template directory not found in package \u2014 bundling error"};let r=nt.join(e,"packages","platform","db-drizzle");return Co(n,r),{applied:!0}}import se from"fs";import St from"path";var bi=["stripe","lemonsqueezy","chinapay","polar"];function Ao(e){se.existsSync(e)&&se.rmSync(e,{recursive:!0,force:!0})}function Pi(e){switch(e){case"stripe":return["stripe"];case"lemon":return["lemonsqueezy"];case"wechat":case"alipay":return["chinapay"]}}function Ci(e,t){let o=St.join(e,".env.example");se.existsSync(o)?se.appendFileSync(o,`
|
|
1864
|
+
`+t):se.writeFileSync(o,t)}function Ii(e){switch(e){case"stripe":return`# Stripe
|
|
1858
1865
|
STRIPE_SECRET_KEY=
|
|
1859
1866
|
STRIPE_WEBHOOK_SECRET=
|
|
1860
1867
|
`;case"lemon":return`# Lemon Squeezy
|
|
@@ -1869,13 +1876,13 @@ CHINAPAY_WECHAT_ENABLED=true
|
|
|
1869
1876
|
CHINAPAY_APP_ID=
|
|
1870
1877
|
CHINAPAY_APP_SECRET=
|
|
1871
1878
|
CHINAPAY_ALIPAY_ENABLED=true
|
|
1872
|
-
`}}async function
|
|
1873
|
-
`),n=[],r=e.includes("#"),
|
|
1874
|
-
`),t)}function
|
|
1875
|
-
`).filter(l=>{let m=l.trim();if(m===""||m.startsWith("//"))return!0;let
|
|
1876
|
-
`);return e.replace(
|
|
1877
|
-
`+r);let
|
|
1878
|
-
`)}}import f from"fs";import d from"path";function
|
|
1879
|
+
`}}async function wo(e,t){let o=St.join(e,"packages","commerce","billing");if(!se.existsSync(o))return;if(t==="none"){Ao(o);return}let n=St.join(o,"src"),r=new Set(Pi(t));for(let i of bi)r.has(i)||Ao(St.join(n,i));let s=St.join(n,"checkout","types.ts");if(se.existsSync(s)){let i=t==="stripe"?"stripe":t==="lemon"?"lemonsqueezy":"chinapay",c=se.readFileSync(s,"utf8"),a=c.replace(/export type CheckoutProviderType\s*=\s*[^;]+;/,`export type CheckoutProviderType = "${i}";`);a!==c&&se.writeFileSync(s,a)}Ci(e,Ii(t))}import de from"fs";import ot from"path";var xo=new Set(["siliconflow","volcengine-ark","bailian","moonshot"]),sn=/@sailor:([^\s*]+)/;function Ai(e,t){let{providerIds:o,customEndpoint:n}=t;if(e.startsWith("provider:")){let r=e.slice(9);return{marker:e,keep:o.includes(r)}}if(e==="cn-compatible"){let r=o.some(s=>xo.has(s));return{marker:e,keep:r||!!n}}if(e.startsWith("cn-instance:")){let r=e.slice(12);return{marker:e,keep:o.includes(r)}}return e==="custom-endpoint"?{marker:e,keep:!!n}:e==="registry-entries"?{marker:e,keep:!0}:{marker:e,keep:!0}}function Ro(e){return e.replace(/\s*\/\/\s*@sailor:[^\n]*$/,"")}function ko(e){return e.replace(/\s*#\s*@sailor:[^\n]*$/,"")}function To(e,t){let o=e.split(`
|
|
1880
|
+
`),n=[],r=e.includes("#"),s=0;for(;s<o.length;){let i=o[s],c=i.match(sn);if(!c){n.push(i),s++;continue}let{marker:a,keep:l}=Ai(c[1],t);if(a==="registry-entries"){n.push(r?ko(i):Ro(i)),s++;continue}if(l){let m=r?ko(i):Ro(i);m.trim().length>0&&n.push(m);let S=s+1;for(;S<o.length&&o[S].trim()!==""&&!sn.test(o[S]);)n.push(o[S]),S++;S<o.length&&o[S].trim()===""&&(n.push(o[S]),S++),s=S}else{let m=s+1;for(;m<o.length&&o[m].trim()!==""&&!sn.test(o[m]);)m++;m<o.length&&o[m].trim()===""&&m++,s=m}}return wi(n.join(`
|
|
1881
|
+
`),t)}function wi(e,t){let{providerIds:o,customEndpoint:n}=t,r=new Set;for(let l of o)["openai","anthropic","google","mistral","xai"].includes(l)&&r.add(l),r.add(l.replace(/-/g,"_")),r.add(l);n&&r.add("custom");let s=/createProviderRegistry\(\{([\s\S]*?)\}\)/,i=e.match(s);if(!i)return e;let a=i[1].split(`
|
|
1882
|
+
`).filter(l=>{let m=l.trim();if(m===""||m.startsWith("//"))return!0;let S=m.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*,?$/);return S?r.has(S[1]):!0}).join(`
|
|
1883
|
+
`);return e.replace(s,`createProviderRegistry({${a}})`)}function Ri(e,t){let o=ot.join(t,"registry.ts.template"),n=de.readFileSync(o,"utf-8");return To(n,e)}function ki(e,t){let o=ot.join(t,".env.example.template"),n=de.readFileSync(o,"utf-8");return To(n,e)}async function No(e,t,o){let n=Ri(t,o),r=ki(t,o),s=ot.join(e,"packages/ai/agents/src");await de.promises.mkdir(s,{recursive:!0}),await de.promises.writeFile(ot.join(s,"registry.ts"),n),await de.promises.appendFile(ot.join(e,".env.example"),`
|
|
1884
|
+
`+r);let i={ai:"latest"},{providerIds:c,customEndpoint:a}=t;(c.includes("openai")||a||c.some(m=>xo.has(m)))&&(i["@ai-sdk/openai"]="latest",i["@ai-sdk/openai-compatible"]="latest"),c.includes("anthropic")&&(i["@ai-sdk/anthropic"]="latest"),c.includes("google")&&(i["@ai-sdk/google"]="latest"),c.includes("mistral")&&(i["@ai-sdk/mistral"]="latest"),c.includes("xai")&&(i["@ai-sdk/openai"]="latest");let l=ot.join(e,"package.json");if(de.existsSync(l)){let m=JSON.parse(await de.promises.readFile(l,"utf-8"));m.dependencies||(m.dependencies={});for(let[S,T]of Object.entries(i))m.dependencies[S]=T;await de.promises.writeFile(l,JSON.stringify(m,null,2)+`
|
|
1885
|
+
`)}}import f from"fs";import d from"path";function Oo(e,t){if(!f.existsSync(e))return;let o=f.readdirSync(e);for(let n of o){let r=d.join(e,n);f.statSync(r).isDirectory()?Oo(r,t):t(r)}}async function Uo(e,t){let o=d.join(e,"package.json");if(!f.existsSync(o))return;let n=JSON.parse(f.readFileSync(o,"utf8"));if(t.orm!=="prisma"){let r=d.join(e,"packages","db");if(f.existsSync(r)){let s=d.join(r,"prisma");f.existsSync(s)&&f.rmSync(s,{recursive:!0,force:!0});let i=d.join(r,"package.json");if(f.existsSync(i)){let a=JSON.parse(f.readFileSync(i,"utf8"));a.dependencies&&delete a.dependencies["@prisma/client"],a.devDependencies&&delete a.devDependencies.prisma,f.writeFileSync(i,JSON.stringify(a,null,2)+`
|
|
1879
1886
|
`)}let c=d.join(r,"src","index.ts");f.existsSync(c)&&f.writeFileSync(c,`
|
|
1880
1887
|
export const prisma = {} as any;
|
|
1881
1888
|
export const db = {} as any;
|
|
@@ -1922,7 +1929,7 @@ export type UsageAggregate = any;
|
|
|
1922
1929
|
export type UsageLedgerEntry = any;
|
|
1923
1930
|
export type UsageType = any;
|
|
1924
1931
|
export type UsageLedgerSource = any;
|
|
1925
|
-
`)}n.scripts?.postinstall&&(n.scripts.postinstall=n.scripts.postinstall.replace(/prisma generate/g,'echo "ORM Disabled"'))}if(!t.i18n){let r=d.join(e,"apps");if(f.existsSync(r)){let
|
|
1932
|
+
`)}n.scripts?.postinstall&&(n.scripts.postinstall=n.scripts.postinstall.replace(/prisma generate/g,'echo "ORM Disabled"'))}if(!t.i18n){let r=d.join(e,"apps");if(f.existsSync(r)){let s=f.readdirSync(r);for(let i of s){let c=d.join(r,i);if(!f.statSync(c).isDirectory())continue;let a=d.join(c,"src","app");if(!f.existsSync(a))continue;["[lang]","[locale]"].forEach(b=>{let u=d.join(a,b);if(f.existsSync(u)){let U=f.readdirSync(u);for(let bt of U)f.renameSync(d.join(u,bt),d.join(a,bt));f.rmdirSync(u)}}),["src/middleware.ts","middleware.ts"].forEach(b=>{let u=d.join(c,b);f.existsSync(u)&&f.rmSync(u)});let l=d.join(c,"src","lib");f.existsSync(l)||f.mkdirSync(l,{recursive:!0});let m=d.join(l,"mock-i18n.ts");f.writeFileSync(m,`
|
|
1926
1933
|
import React from 'react';
|
|
1927
1934
|
export function useTranslations(namespace?: string): any { return (key: string) => key; }
|
|
1928
1935
|
export function useLocale() { return 'en'; }
|
|
@@ -1940,7 +1947,7 @@ export const useRouter = () => ({ push: (...args: any[]) => {}, replace: (...arg
|
|
|
1940
1947
|
export default function createMiddleware(...args: any[]) { return (req: any) => req; }
|
|
1941
1948
|
export function createNavigation(...args: any[]) { return { Link, redirect, usePathname, useRouter, getPathname: () => '/' }; }
|
|
1942
1949
|
export function defineRouting<T>(config: T): T { return config; }
|
|
1943
|
-
`);let
|
|
1950
|
+
`);let T=d.join(l,"mock-i18n.d.ts");f.writeFileSync(T,`declare module '@/lib/mock-i18n' {
|
|
1944
1951
|
export function useTranslations(namespace?: string): any;
|
|
1945
1952
|
export function useLocale(): string;
|
|
1946
1953
|
export const NextIntlClientProvider: React.FC<any>;
|
|
@@ -1963,14 +1970,14 @@ export * from './mock-i18n';
|
|
|
1963
1970
|
export { default } from './mock-i18n';
|
|
1964
1971
|
`);let N=d.join(c,"tsconfig.json");if(f.existsSync(N))try{let b=f.readFileSync(N,"utf8");if(b.includes('"paths": {')){let u=b.replace(/"paths":\s*\{/,`"paths": {
|
|
1965
1972
|
"next-intl": ["./src/lib/mock-i18n"],
|
|
1966
|
-
"next-intl/*": ["./src/lib/mock-i18n"],`);f.writeFileSync(N,u)}}catch{}let
|
|
1973
|
+
"next-intl/*": ["./src/lib/mock-i18n"],`);f.writeFileSync(N,u)}}catch{}let D=d.join(c,"src");f.existsSync(D)&&Oo(D,b=>{if(!b.endsWith(".ts")&&!b.endsWith(".tsx"))return;let u=f.readFileSync(b,"utf8"),U=!1;b.endsWith("src/app/layout.tsx")&&u.includes("../providers")&&(u=u.replace(/from\s+['"]\.\.\/providers['"]/g,"from './providers'"),U=!0),u=u.replace(/params:\s*Promise<\{[^}]*(lang|locale)[^}]*\}>/g,"params: any"),u=u.replace(/\(\{\s*params\s*\}\s*:\s*\{\s*params:\s*any\s*\}\)/g,"({ params }: any)"),u.match(/locale\s*:\s*any/)&&(u=u.replace(/locale\s*:\s*any/g,"locale: string"),U=!0),u.match(/lang\s*:\s*any/)&&(u=u.replace(/lang\s*:\s*any/g,"lang: string"),U=!0),u.match(/requestLocale\s*:\s*any/)&&(u=u.replace(/requestLocale\s*:\s*any/g,"requestLocale: string"),U=!0),u.match(/\(locale\s*=\s*(.*)\)/)&&(u=u.replace(/\(locale\s*=\s*(.*)\)/g,"() "),U=!0),u.match(/export\s+default\s+async\s+function\s+LangLayout/)&&(u=u.replace(/export\s+default\s+async\s+function\s+LangLayout\s*\(\s*\{\s*children\s*,\s*params\s*\}\s*:\s*LangLayoutProps\s*\)/g,"export default async function LangLayout({ children }: { children: React.ReactNode })"),U=!0),(b.endsWith("src/app/layout.tsx")||b.endsWith("src/app/sitemap.ts")||b.endsWith("src/app/robots.ts")||b.endsWith("src/components/ui/locale-switcher.tsx")||b.endsWith("src/app/(marketing)/blog/[slug]/page.tsx"))&&(u=u.replace(/const\s+\{\s*lang\s*\}\s*=\s*(await\s+)?params;?/g,'const lang = "en";'),u=u.replace(/const\s+\{\s*locale\s*\}\s*=\s*(await\s+)?params;?/g,'const locale = "en";'),u=u.replace(/const\s+locale\s*=\s*lang\s+as\s+Locale;?/g,'const locale = "en";'),u=u.replace(/\(l\)\s*=>/g,"(l: any) =>"),U=!0),b.endsWith("src/i18n/request.ts")&&(u=u.replace(/async\s*\(\{\s*requestLocale\s*\}\)/g,"async ({ requestLocale }: any)"),U=!0),(u.match(/locale\s*:/)||u.match(/lang\s*:/))&&(U=!0),U&&f.writeFileSync(b,u)});let ie=d.join(c,"package.json");if(f.existsSync(ie)){let b=JSON.parse(f.readFileSync(ie,"utf8"));b.dependencies&&delete b.dependencies["next-intl"],f.writeFileSync(ie,JSON.stringify(b,null,2)+`
|
|
1967
1974
|
`)}}}}f.writeFileSync(o,JSON.stringify(n,null,2)+`
|
|
1968
|
-
`)}function
|
|
1969
|
-
`)}function
|
|
1970
|
-
`);
|
|
1971
|
-
`)}async function
|
|
1972
|
-
`);
|
|
1973
|
-
`)}async function
|
|
1975
|
+
`)}function _(e){try{f.rmSync(e,{recursive:!0,force:!0})}catch{}}function xi(e,t){if(!f.existsSync(e))return;let o;try{o=JSON.parse(f.readFileSync(e,"utf8"))}catch{return}let n=!1;if(o.dependencies&&t in o.dependencies){let{[t]:r,...s}=o.dependencies;o={...o,dependencies:s},n=!0}if(o.devDependencies&&t in o.devDependencies){let{[t]:r,...s}=o.devDependencies;o={...o,devDependencies:s},n=!0}n&&f.writeFileSync(e,JSON.stringify(o,null,2)+`
|
|
1976
|
+
`)}function Lo(e,t){t.cronJobs||(_(d.join(e,"apps/web/src/app/api/cron")),_(d.join(e,"packages/integrations/queue/src/scheduled"))),t.auditLog||(_(d.join(e,"apps/web/src/app/[locale]/(app)/settings/audit-log")),_(d.join(e,"apps/web/src/app/api/audit-logs")),_(d.join(e,"apps/web/src/components/audit"))),t.apiKeys||(_(d.join(e,"apps/web/src/app/[locale]/(app)/settings/api-keys")),_(d.join(e,"apps/web/src/app/api/api-keys")),_(d.join(e,"apps/web/src/components/api-keys"))),t.webhooks||(_(d.join(e,"apps/web/src/app/[locale]/(app)/settings/webhooks")),_(d.join(e,"apps/web/src/app/api/webhooks")),_(d.join(e,"apps/web/src/components/webhooks")),_(d.join(e,"packages/webhooks"))),t.commandPalette||(_(d.join(e,"apps/web/src/components/command-palette")),_(d.join(e,"apps/web/src/app/[locale]/providers/command-palette-mount.tsx"))),t.cookieConsent||(_(d.join(e,"apps/landing-page/src/components/cookie-consent-banner.tsx")),_(d.join(e,"apps/landing-page/src/components/__tests__/cookie-consent-banner.test.tsx")),_(d.join(e,"apps/landing-page/src/lib/cookie-consent.ts")),_(d.join(e,"apps/landing-page/src/lib/__tests__/cookie-consent.test.ts")),_(d.join(e,"apps/web/src/app/api/cookie-consent"))),t.legalPages||(_(d.join(e,"apps/landing-page/src/app/[lang]/(legal)/legal")),_(d.join(e,"apps/landing-page/src/lib/legal-documents.ts")),_(d.join(e,"apps/landing-page/src/lib/__tests__/legal-documents.test.ts"))),t.chinaCompliance||(_(d.join(e,"packages/china-compliance")),_(d.join(e,"apps/landing-page/src/components/icp-footer.tsx")),_(d.join(e,"apps/landing-page/src/components/__tests__/icp-footer.test.tsx")),_(d.join(e,"apps/web/src/components/auth/wechat-login-button.tsx")),_(d.join(e,"apps/web/src/components/auth/__tests__/wechat-login-button.test.tsx")),xi(d.join(e,"apps/web/package.json"),"@nebutra/china-compliance"))}import{getSchema as Ti,printSchema as Ni}from"@mrleebo/prisma-ast";var Oi=/^\/\/\/\s*@conditional\(([A-Za-z][A-Za-z0-9_-]*)=([^)]+)\)\s*$/;function Ui(e){return e.type==="comment"&&e.text.trim().startsWith("///")}function Li(e){let t=Oi.exec(e.trim());if(!t)return null;let[,o,n]=t,r=n.split("|").map(s=>s.trim()).filter(s=>s.length>0);return{flag:o,values:r}}function Mi(e,t){for(let o=t-1;o>=0;o--){let n=e[o];if(n.type==="comment"){let r=Li(n.text);if(r)return{annIndex:o,parsed:r};continue}break}return null}function an(e,t){let o=Ti(e),n=new Set;for(let c=0;c<o.list.length;c++){if(o.list[c].type!=="model")continue;let l=Mi(o.list,c);if(!l)continue;let m=t[l.parsed.flag];if(m!==void 0&&l.parsed.values.includes(m))n.add(l.annIndex);else{n.add(c);for(let N=c-1;N>=0;N--){let D=o.list[N];if(D.type==="comment"&&Ui(D)){n.add(N);continue}break}let T=o.list[c+1];T&&T.type==="break"&&n.add(c+1)}}let r=o.list.filter((c,a)=>!n.has(a)),s=[];for(let c of r){let a=s[s.length-1];c.type==="break"&&a&&a.type==="break"||s.push(c)}return Ni({type:"schema",list:s})}import rt from"fs";import Do from"path";var jt=[{id:"qstash",name:"Upstash QStash",region:"both",envVars:["QSTASH_TOKEN","QSTASH_CURRENT_SIGNING_KEY","QSTASH_NEXT_SIGNING_KEY"],docs:"https://upstash.com/docs/qstash",description:"Serverless HTTP-based queue"},{id:"bullmq",name:"BullMQ",region:"both",envVars:["REDIS_URL"],docs:"https://docs.bullmq.io",description:"Redis-backed job queue"},{id:"sqs",name:"AWS SQS",region:"global",envVars:["AWS_SQS_QUEUE_URL","AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_REGION"],docs:"https://docs.aws.amazon.com/AWSSimpleQueueService/",description:"AWS managed queue"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:"Skip queue package"}];function Mo(e){return jt.find(t=>t.id===e)}var Qp=jt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Fo(e){return rt.existsSync(e)?rt.readFileSync(e,"utf-8"):null}function Di(e,t){let o=Do.join(e,".env.example"),n=Fo(o);if(n===null)return;let r=`# Queue: ${t.name}`;if(n.includes(r))return;let s=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
1977
|
+
`);rt.appendFileSync(o,s)}function Fi(e,t,o){let n=Do.join(e,".env.example"),r=Fo(n);if(r===null)return;let s=new RegExp(`^${t}=.*$`,"m");s.test(r)?rt.writeFileSync(n,r.replace(s,`${t}="${o}"`)):rt.appendFileSync(n,`${t}="${o}"
|
|
1978
|
+
`)}async function jo(e,t,o){let n=be(e,"queue");if(t==="none"){n&&rt.rmSync(n,{recursive:!0,force:!0});return}let r=Mo(t);if(!r)throw new Error(`Unknown queue provider "${t}". Valid ids: ${jt.map(s=>s.id).join(", ")}`);n&&(Di(e,r),Fi(e,"QUEUE_PROVIDER",t))}import st from"fs";import Ko from"path";var Vt=[{id:"meilisearch",name:"Meilisearch",region:"both",envVars:["MEILISEARCH_HOST","MEILISEARCH_MASTER_KEY"],docs:"https://docs.meilisearch.com",description:"Self-hostable search"},{id:"typesense",name:"Typesense",region:"both",envVars:["TYPESENSE_HOST","TYPESENSE_PORT","TYPESENSE_PROTOCOL","TYPESENSE_API_KEY"],docs:"https://typesense.org/docs",description:"Open-source search"},{id:"algolia",name:"Algolia",region:"global",envVars:["ALGOLIA_APP_ID","ALGOLIA_ADMIN_API_KEY","ALGOLIA_SEARCH_API_KEY"],docs:"https://www.algolia.com/doc/",description:"Managed search as a service"},{id:"pgvector",name:"pgvector (Postgres)",region:"both",envVars:["DATABASE_URL"],docs:"https://github.com/pgvector/pgvector",description:"Postgres vector + full-text"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:"Skip search package"}];function Vo(e){return Vt.find(t=>t.id===e)}var sl=Vt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function Bo(e){return st.existsSync(e)?st.readFileSync(e,"utf-8"):null}function ji(e,t){let o=Ko.join(e,".env.example"),n=Bo(o);if(n===null)return;let r=`# Search: ${t.name}`;if(n.includes(r))return;let s=["","# =============================================",r,`# Docs: ${t.docs}`,"# =============================================",...t.envVars.map(i=>`${i}=""`),""].join(`
|
|
1979
|
+
`);st.appendFileSync(o,s)}function Vi(e,t,o){let n=Ko.join(e,".env.example"),r=Bo(n);if(r===null)return;let s=new RegExp(`^${t}=.*$`,"m");s.test(r)?st.writeFileSync(n,r.replace(s,`${t}="${o}"`)):st.appendFileSync(n,`${t}="${o}"
|
|
1980
|
+
`)}async function $o(e,t,o){let n=be(e,"search");if(t==="none"){n&&st.rmSync(n,{recursive:!0,force:!0});return}let r=Vo(t);if(!r)throw new Error(`Unknown search provider "${t}". Valid ids: ${Vt.map(s=>s.id).join(", ")}`);n&&(ji(e,r),Vi(e,"SEARCH_PROVIDER",t))}import ue from"fs";import xe from"path";function Ki(e){let t=[xe.join(e,"packages","db"),xe.join(e,"apps","web")];for(let o of t){let n=xe.join(o,"prisma","schema.prisma");if(ue.existsSync(n))return{packageDir:o,seedPath:xe.join(o,"prisma","seed.ts"),packageJsonPath:xe.join(o,"package.json")}}return null}function Bi(e){try{let t=ue.readFileSync(e,"utf8"),o=new Set,n=/^\s*model\s+(\w+)\s*\{/gm,r=n.exec(t);for(;r;)o.add(r[1]),r=n.exec(t);return o}catch{return new Set}}function $i(e,t){return!t||e==="none"?` // Skipping admin user \u2014 auth=${JSON.stringify(e)} (or no User model).`:e==="clerk"?` try {
|
|
1974
1981
|
const admin = await prisma.user.create({
|
|
1975
1982
|
data: {
|
|
1976
1983
|
clerkId: "user_mock_admin",
|
|
@@ -2001,7 +2008,7 @@ export { default } from './mock-i18n';
|
|
|
2001
2008
|
console.log("\\u2713 Created admin user:", admin.email);
|
|
2002
2009
|
} catch (err) {
|
|
2003
2010
|
console.warn("\\u26A0 Admin user creation skipped:", (err as Error).message);
|
|
2004
|
-
}`}function
|
|
2011
|
+
}`}function Yi(e,t){let o=t.has("Tenant"),n=t.has("User"),r=t.has("Project"),s=$i(e,n),i=o?` let tenants: Array<{ id: string; name: string }> = [];
|
|
2005
2012
|
try {
|
|
2006
2013
|
tenants = await Promise.all([
|
|
2007
2014
|
prisma.tenant.create({
|
|
@@ -2051,10 +2058,10 @@ async function main(): Promise<void> {
|
|
|
2051
2058
|
console.log("\\uD83C\\uDF31 Seeding database...");
|
|
2052
2059
|
|
|
2053
2060
|
// --- Tenants ---------------------------------------------------------
|
|
2054
|
-
${
|
|
2061
|
+
${i}
|
|
2055
2062
|
|
|
2056
2063
|
// --- Admin user ------------------------------------------------------
|
|
2057
|
-
${
|
|
2064
|
+
${s}
|
|
2058
2065
|
|
|
2059
2066
|
// --- Sample projects per tenant --------------------------------------
|
|
2060
2067
|
${c}
|
|
@@ -2070,12 +2077,12 @@ main()
|
|
|
2070
2077
|
.finally(async () => {
|
|
2071
2078
|
await prisma.$disconnect();
|
|
2072
2079
|
});
|
|
2073
|
-
`}function
|
|
2074
|
-
`)}catch(t){let o=t instanceof Error?t.message:String(t);console.warn(`[create-sailor] Failed to patch seed scripts in ${e}: ${o}`)}}async function
|
|
2075
|
-
`+t):
|
|
2080
|
+
`}function qi(e){if(ue.existsSync(e))try{let t=ue.readFileSync(e,"utf8"),o=JSON.parse(t),n=o.scripts??{},r=!1;n["db:seed"]||(n["db:seed"]="prisma db seed",r=!0);let s=o.prisma??{};if(s.seed||(s.seed="tsx prisma/seed.ts",r=!0),!r)return;let i={...o,scripts:n,prisma:s};ue.writeFileSync(e,JSON.stringify(i,null,2)+`
|
|
2081
|
+
`)}catch(t){let o=t instanceof Error?t.message:String(t);console.warn(`[create-sailor] Failed to patch seed scripts in ${e}: ${o}`)}}async function Yo(e,t){let o=Ki(e);if(o)try{let n=xe.join(o.packageDir,"prisma","schema.prisma"),r=Bi(n),s=Yi(t,r),i=xe.dirname(o.seedPath);ue.existsSync(i)||ue.mkdirSync(i,{recursive:!0}),ue.writeFileSync(o.seedPath,s),qi(o.packageJsonPath)}catch(n){let r=n instanceof Error?n.message:String(n);throw console.error(`[create-sailor] Failed to generate seed data: ${r}`),new Error(`Unable to write prisma/seed.ts: ${r}`)}}import me from"fs";import Te from"path";var Kt=[{id:"twilio",name:"Twilio",region:"global",envVars:["TWILIO_ACCOUNT_SID","TWILIO_AUTH_TOKEN","TWILIO_FROM_NUMBER"],docs:"https://www.twilio.com/docs/sms",pkgs:["twilio"]},{id:"messagebird",name:"MessageBird (Bird)",region:"global",envVars:["MESSAGEBIRD_ACCESS_KEY","MESSAGEBIRD_ORIGINATOR"],docs:"https://developers.messagebird.com/api/sms-messaging/",pkgs:["messagebird"]},{id:"plivo",name:"Plivo",region:"global",envVars:["PLIVO_AUTH_ID","PLIVO_AUTH_TOKEN","PLIVO_FROM_NUMBER"],docs:"https://www.plivo.com/docs/sms/",pkgs:["plivo"]},{id:"aliyun-sms",name:"\u963F\u91CC\u4E91\u77ED\u4FE1 Aliyun SMS",region:"cn",envVars:["ALIYUN_SMS_ACCESS_KEY","ALIYUN_SMS_ACCESS_SECRET","ALIYUN_SMS_SIGN_NAME","ALIYUN_SMS_TEMPLATE_CODE","ALIYUN_SMS_ENDPOINT"],docs:"https://help.aliyun.com/product/44282.html",pkgs:["@alicloud/dysmsapi20170525","@alicloud/openapi-client"]},{id:"tencent-sms",name:"\u817E\u8BAF\u4E91\u77ED\u4FE1 Tencent SMS",region:"cn",envVars:["TENCENT_SMS_SECRET_ID","TENCENT_SMS_SECRET_KEY","TENCENT_SMS_SDK_APP_ID","TENCENT_SMS_SIGN_NAME","TENCENT_SMS_TEMPLATE_ID","TENCENT_SMS_REGION"],docs:"https://cloud.tencent.com/document/product/382",pkgs:["tencentcloud-sdk-nodejs-sms"]},{id:"yunpian",name:"\u4E91\u7247 Yunpian",region:"cn",envVars:["YUNPIAN_API_KEY","YUNPIAN_SIGN"],docs:"https://www.yunpian.com/official/document/sms/zh_CN/introduction_product_intro",pkgs:["yunpian-nodejs-sdk"]},{id:"none",name:"None",region:"both",envVars:[],docs:""}],fl=Kt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});function qo(e){return Kt.find(t=>t.id===e)}var Gi="@nebutra/sms",Hi={twilio:"^5.3.6",messagebird:"^4.0.1",plivo:"^4.73.0","@alicloud/dysmsapi20170525":"^2.0.24","@alicloud/openapi-client":"^0.4.12","tencentcloud-sdk-nodejs-sms":"^4.0.970","yunpian-nodejs-sdk":"^0.0.2"};function cn(e,t){me.mkdirSync(Te.dirname(e),{recursive:!0}),me.writeFileSync(e,t)}function Wi(e,t){let o=Te.join(e,".env.example");me.existsSync(o)?me.appendFileSync(o,`
|
|
2082
|
+
`+t):me.writeFileSync(o,t)}function Xi(e){if(e.envVars.length===0)return"";let t=`# SMS provider: ${e.name}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
2076
2083
|
`);return`${t}
|
|
2077
2084
|
${o}
|
|
2078
|
-
`}var
|
|
2085
|
+
`}var zi=`/**
|
|
2079
2086
|
* Inputs for the \`sendSms\` wrapper. Keep this surface portable across
|
|
2080
2087
|
* providers \u2014 provider-specific options belong in the wrapper itself.
|
|
2081
2088
|
*/
|
|
@@ -2095,7 +2102,7 @@ export interface SendSmsResult {
|
|
|
2095
2102
|
provider: string;
|
|
2096
2103
|
raw?: unknown;
|
|
2097
2104
|
}
|
|
2098
|
-
`;function
|
|
2105
|
+
`;function Ji(){return`import twilio from "twilio";
|
|
2099
2106
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2100
2107
|
|
|
2101
2108
|
let client: ReturnType<typeof twilio> | null = null;
|
|
@@ -2130,7 +2137,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2130
2137
|
}
|
|
2131
2138
|
|
|
2132
2139
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2133
|
-
`}function
|
|
2140
|
+
`}function Qi(){return`import messagebird from "messagebird";
|
|
2134
2141
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2135
2142
|
|
|
2136
2143
|
let client: ReturnType<typeof messagebird> | null = null;
|
|
@@ -2168,7 +2175,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2168
2175
|
}
|
|
2169
2176
|
|
|
2170
2177
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2171
|
-
`}function
|
|
2178
|
+
`}function Zi(){return`import plivo from "plivo";
|
|
2172
2179
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2173
2180
|
|
|
2174
2181
|
let client: InstanceType<typeof plivo.Client> | null = null;
|
|
@@ -2207,7 +2214,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2207
2214
|
}
|
|
2208
2215
|
|
|
2209
2216
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2210
|
-
`}function
|
|
2217
|
+
`}function ea(){return`import Dysmsapi, { SendSmsRequest } from "@alicloud/dysmsapi20170525";
|
|
2211
2218
|
import * as $OpenApi from "@alicloud/openapi-client";
|
|
2212
2219
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2213
2220
|
|
|
@@ -2256,7 +2263,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2256
2263
|
}
|
|
2257
2264
|
|
|
2258
2265
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2259
|
-
`}function
|
|
2266
|
+
`}function ta(){return`import * as tencentcloud from "tencentcloud-sdk-nodejs-sms";
|
|
2260
2267
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2261
2268
|
|
|
2262
2269
|
const SmsClient = tencentcloud.sms.v20210111.Client;
|
|
@@ -2321,7 +2328,7 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2321
2328
|
}
|
|
2322
2329
|
|
|
2323
2330
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2324
|
-
`}function
|
|
2331
|
+
`}function na(){return`// @ts-expect-error \u2014 yunpian-nodejs-sdk ships no type declarations
|
|
2325
2332
|
import Yunpian from "yunpian-nodejs-sdk";
|
|
2326
2333
|
import type { SendSmsInput, SendSmsResult } from "./types";
|
|
2327
2334
|
|
|
@@ -2373,13 +2380,13 @@ export async function sendSms(input: SendSmsInput): Promise<SendSmsResult> {
|
|
|
2373
2380
|
}
|
|
2374
2381
|
|
|
2375
2382
|
export type { SendSmsInput, SendSmsResult } from "./types";
|
|
2376
|
-
`}function
|
|
2377
|
-
`}}function
|
|
2378
|
-
`}async function
|
|
2379
|
-
`+t):
|
|
2383
|
+
`}function oa(e){switch(e){case"twilio":return Ji();case"messagebird":return Qi();case"plivo":return Zi();case"aliyun-sms":return ea();case"tencent-sms":return ta();case"yunpian":return na();case"none":return`export {};
|
|
2384
|
+
`}}function ra(e){let t={};for(let n of e.pkgs??[]){let r=Hi[n];r&&(t[n]=r)}let o={name:Gi,version:"0.1.0",private:!0,description:`Transactional SMS via ${e.name}`,type:"module",main:"./dist/index.js",exports:{".":{import:"./dist/index.js",types:"./dist/index.d.ts"}},scripts:{build:"tsup",typecheck:"tsc --noEmit",clean:"rm -rf dist .turbo"},dependencies:t,devDependencies:{tsup:"^8.5.0",typescript:"catalog:"}};return JSON.stringify(o,null,2)+`
|
|
2385
|
+
`}async function Go(e,t,o="global"){if(t==="none")return;let n=qo(t);if(!n)throw new Error(`Unknown SMS provider "${t}". Valid ids: ${Kt.map(i=>i.id).join(", ")}`);let r=Te.join(e,"packages","sms");if(!me.existsSync(r))return;let s=Te.join(r,"src");me.existsSync(s)&&me.rmSync(s,{recursive:!0,force:!0});try{cn(Te.join(r,"package.json"),ra(n)),cn(Te.join(s,"types.ts"),zi),cn(Te.join(s,"index.ts"),oa(n.id)),Wi(e,Xi(n))}catch(i){throw console.error(`Failed to apply SMS selection "${t}":`,i),new Error(`SMS scaffold for "${t}" failed \u2014 see stderr for details.`)}}import Oe from"fs";import Ne from"path";var ht=[{id:"r2",name:"Cloudflare R2",region:"global",envVars:["R2_ACCOUNT_ID","R2_ACCESS_KEY_ID","R2_SECRET_ACCESS_KEY","R2_BUCKET"],docs:"https://developers.cloudflare.com/r2/api/s3/api/",pkg:"@aws-sdk/client-s3",pkgVersion:"^3.658.0"},{id:"s3",name:"AWS S3",region:"global",envVars:["AWS_ACCESS_KEY_ID","AWS_SECRET_ACCESS_KEY","AWS_S3_REGION","S3_BUCKET"],docs:"https://docs.aws.amazon.com/s3/",pkg:"@aws-sdk/client-s3",pkgVersion:"^3.658.0"},{id:"supabase-storage",name:"Supabase Storage",region:"global",envVars:["SUPABASE_URL","SUPABASE_SERVICE_ROLE_KEY","SUPABASE_BUCKET"],docs:"https://supabase.com/docs/guides/storage",pkg:"@supabase/supabase-js",pkgVersion:"^2.45.4"},{id:"aliyun-oss",name:"\u963F\u91CC\u4E91 OSS",region:"cn",envVars:["ALIYUN_OSS_ACCESS_KEY","ALIYUN_OSS_ACCESS_SECRET","ALIYUN_OSS_REGION","ALIYUN_OSS_BUCKET"],docs:"https://help.aliyun.com/product/31815.html",pkg:"ali-oss",pkgVersion:"^6.21.0"},{id:"tencent-cos",name:"\u817E\u8BAF\u4E91 COS",region:"cn",envVars:["TENCENT_COS_SECRET_ID","TENCENT_COS_SECRET_KEY","TENCENT_COS_BUCKET","TENCENT_COS_REGION"],docs:"https://cloud.tencent.com/document/product/436",pkg:"cos-nodejs-sdk-v5",pkgVersion:"^2.13.7"},{id:"qiniu",name:"\u4E03\u725B\u4E91 Kodo",region:"cn",envVars:["QINIU_ACCESS_KEY","QINIU_SECRET_KEY","QINIU_BUCKET","QINIU_DOMAIN"],docs:"https://developer.qiniu.com/kodo",pkg:"qiniu",pkgVersion:"^7.14.0"},{id:"none",name:"None",region:"global",envVars:[],docs:""}];function pn(e){let t=e==="supabase"?"supabase-storage":e;return ht.some(o=>o.id===t)?t:void 0}function Ho(e){let t=pn(e);return t?ht.find(o=>o.id===t):void 0}var _l=ht.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});var Ue="@nebutra/storage";function ln(e,t){Oe.mkdirSync(Ne.dirname(e),{recursive:!0}),Oe.writeFileSync(e,t)}function sa(e,t){let o=Ne.join(e,".env.example");Oe.existsSync(o)?Oe.appendFileSync(o,`
|
|
2386
|
+
`+t):Oe.writeFileSync(o,t)}function ia(e){if(e.envVars.length===0)return"";let t=`# Storage provider: ${e.name}`,o=e.envVars.map(n=>`${n}=`).join(`
|
|
2380
2387
|
`);return`${t}
|
|
2381
2388
|
${o}
|
|
2382
|
-
`}var
|
|
2389
|
+
`}var aa=`/**
|
|
2383
2390
|
* Inputs for the \`uploadFile\` wrapper. Keep this surface portable across
|
|
2384
2391
|
* providers \u2014 provider-specific options belong in the wrapper itself.
|
|
2385
2392
|
*/
|
|
@@ -2405,8 +2412,8 @@ export interface SignedUrlOptions {
|
|
|
2405
2412
|
operation?: "get" | "put";
|
|
2406
2413
|
contentType?: string;
|
|
2407
2414
|
}
|
|
2408
|
-
`;function
|
|
2409
|
-
* @${
|
|
2415
|
+
`;function ca(){return`/**
|
|
2416
|
+
* @${Ue.slice(1)} \u2014 Cloudflare R2 via the S3-compatible API.
|
|
2410
2417
|
*
|
|
2411
2418
|
* Docs: https://developers.cloudflare.com/r2/api/s3/api/
|
|
2412
2419
|
*/
|
|
@@ -2469,8 +2476,8 @@ export async function getSignedUrl(
|
|
|
2469
2476
|
}
|
|
2470
2477
|
|
|
2471
2478
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2472
|
-
`}function
|
|
2473
|
-
* @${
|
|
2479
|
+
`}function pa(){return`/**
|
|
2480
|
+
* @${Ue.slice(1)} \u2014 AWS S3 wrapper.
|
|
2474
2481
|
*
|
|
2475
2482
|
* Docs: https://docs.aws.amazon.com/s3/
|
|
2476
2483
|
*/
|
|
@@ -2534,8 +2541,8 @@ export async function getSignedUrl(
|
|
|
2534
2541
|
}
|
|
2535
2542
|
|
|
2536
2543
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2537
|
-
`}function
|
|
2538
|
-
* @${
|
|
2544
|
+
`}function la(){return`/**
|
|
2545
|
+
* @${Ue.slice(1)} \u2014 Supabase Storage wrapper.
|
|
2539
2546
|
*
|
|
2540
2547
|
* Docs: https://supabase.com/docs/guides/storage
|
|
2541
2548
|
*/
|
|
@@ -2597,8 +2604,8 @@ export async function getSignedUrl(
|
|
|
2597
2604
|
}
|
|
2598
2605
|
|
|
2599
2606
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2600
|
-
`}function
|
|
2601
|
-
* @${
|
|
2607
|
+
`}function da(){return`/**
|
|
2608
|
+
* @${Ue.slice(1)} \u2014 \u963F\u91CC\u4E91 OSS wrapper via ali-oss.
|
|
2602
2609
|
*
|
|
2603
2610
|
* Docs: https://help.aliyun.com/product/31815.html
|
|
2604
2611
|
*/
|
|
@@ -2644,8 +2651,8 @@ export async function getSignedUrl(
|
|
|
2644
2651
|
}
|
|
2645
2652
|
|
|
2646
2653
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2647
|
-
`}function
|
|
2648
|
-
* @${
|
|
2654
|
+
`}function ua(){return`/**
|
|
2655
|
+
* @${Ue.slice(1)} \u2014 \u817E\u8BAF\u4E91 COS wrapper via cos-nodejs-sdk-v5.
|
|
2649
2656
|
*
|
|
2650
2657
|
* Docs: https://cloud.tencent.com/document/product/436
|
|
2651
2658
|
*/
|
|
@@ -2699,8 +2706,8 @@ export async function getSignedUrl(
|
|
|
2699
2706
|
}
|
|
2700
2707
|
|
|
2701
2708
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2702
|
-
`}function
|
|
2703
|
-
* @${
|
|
2709
|
+
`}function ma(){return`/**
|
|
2710
|
+
* @${Ue.slice(1)} \u2014 \u4E03\u725B\u4E91 Kodo wrapper.
|
|
2704
2711
|
*
|
|
2705
2712
|
* Docs: https://developer.qiniu.com/kodo
|
|
2706
2713
|
*/
|
|
@@ -2767,9 +2774,9 @@ export async function getSignedUrl(
|
|
|
2767
2774
|
}
|
|
2768
2775
|
|
|
2769
2776
|
export type { UploadFileInput, UploadFileResult, SignedUrlOptions } from "./types";
|
|
2770
|
-
`}function
|
|
2771
|
-
`}}function
|
|
2772
|
-
`}async function
|
|
2777
|
+
`}function ga(e){switch(e){case"r2":return ca();case"s3":return pa();case"supabase-storage":return la();case"aliyun-oss":return da();case"tencent-cos":return ua();case"qiniu":return ma();case"none":return`export {};
|
|
2778
|
+
`}}function fa(e){let t={};e.pkg&&e.pkgVersion&&(t[e.pkg]=e.pkgVersion),(e.id==="r2"||e.id==="s3")&&(t["@aws-sdk/s3-request-presigner"]="^3.658.0");let o={name:Ue,version:"0.1.0",private:!0,description:`Object storage via ${e.name}`,type:"module",main:"./dist/index.js",exports:{".":{import:"./dist/index.js",types:"./dist/index.d.ts"}},scripts:{build:"tsup",typecheck:"tsc --noEmit",clean:"rm -rf dist .turbo"},dependencies:t,devDependencies:{tsup:"^8.5.0",typescript:"catalog:"}};return JSON.stringify(o,null,2)+`
|
|
2779
|
+
`}async function Wo(e,t,o){if(t==="none")return;let n=Ho(t);if(!n)throw new Error(`Unknown storage provider "${t}". Valid ids: ${ht.map(i=>i.id).join(", ")}`);let r=Ne.join(e,"packages","storage"),s=Ne.join(r,"src");Oe.existsSync(s)&&Oe.rmSync(s,{recursive:!0,force:!0}),ln(Ne.join(r,"package.json"),fa(n)),ln(Ne.join(s,"types.ts"),aa),ln(Ne.join(s,"index.ts"),ga(n.id)),sa(e,ia(n))}function ge(e,t){if(e===void 0)return t;let o=String(e).trim().toLowerCase();return o==="true"||o==="1"||o==="yes"||o==="on"?!0:o==="false"||o==="0"||o==="no"||o==="off"?!1:t}function Xo(e,t){let o=t==="cn";return{cronJobs:ge(e.cronJobs,!0),auditLog:ge(e.auditLog,!0),apiKeys:ge(e.apiKeys,!0),webhooks:ge(e.webhooks,!0),commandPalette:ge(e.commandPalette,!0),cookieConsent:ge(e.cookieConsent,!0),legalPages:ge(e.legalPages,!0),chinaCompliance:ge(e.chinaCompliance,o)}}var Bt=[{id:"svix",name:"Svix",region:"global",envVars:["SVIX_API_TOKEN"],docs:"https://docs.svix.com",description:"Managed webhooks platform"},{id:"custom",name:"Custom (self-hosted)",region:"both",envVars:["WEBHOOK_SIGNING_SECRET"],docs:"",description:"Custom implementation with built-in package"},{id:"none",name:"None",region:"both",envVars:[],docs:"",description:""}];function zo(e){return Bt.find(t=>t.id===e)}var Rl=Bt.reduce((e,t)=>(e[t.region]||(e[t.region]=[]),e[t.region].push(t),e),{});async function Jo(e,t,o="global"){let n=zo(t);if(!n)throw new Error(`Unknown webhooks provider "${t}". Valid ids: ${Bt.map(r=>r.id).join(", ")}`);if(n.id==="none"){H(e,"webhooks");return}n.envVars.length>0&&Q(e,{category:"Webhooks",name:n.name,envVars:n.envVars,docs:n.docs}),Z(e,"WEBHOOKS_PROVIDER",n.id)}import Le from"fs";import Et from"path";function ya(e){return`import Link from "next/link";
|
|
2773
2780
|
|
|
2774
2781
|
export default function WelcomePage() {
|
|
2775
2782
|
const projectName = ${JSON.stringify(e)};
|
|
@@ -2878,21 +2885,21 @@ function Step({
|
|
|
2878
2885
|
</li>
|
|
2879
2886
|
);
|
|
2880
2887
|
}
|
|
2881
|
-
`}function
|
|
2888
|
+
`}function Sa(e={}){return`
|
|
2882
2889
|
## What you can do next
|
|
2883
2890
|
|
|
2884
2891
|
${[e.apiKeys===!1?"- Manage API keys at `/settings/api-keys` (disabled \u2014 re-enable with `--api-keys=true`)":"- Manage API keys at `/settings/api-keys`",e.auditLog===!1?"- View audit log at `/settings/audit-log` (disabled \u2014 re-enable with `--audit-log=true`)":"- View audit log at `/settings/audit-log`","- Configure webhooks at `/settings/webhooks`",e.commandPalette===!1?"- Press \u2318K to open the command palette (disabled \u2014 re-enable with `--command-palette=true`)":"- Press \u2318K to open the command palette","- Edit notification preferences at `/settings/notifications`","- Export your data at `/settings/account/export`","- For China deployments, see `packages/ops/china-compliance/README.md`"].join(`
|
|
2885
2892
|
`)}
|
|
2886
|
-
`}function
|
|
2893
|
+
`}function ha(e=[]){return e.length===0?"":`
|
|
2887
2894
|
## Production readiness holds
|
|
2888
2895
|
|
|
2889
2896
|
Do not enable these in production until you replace stubs, add provider credentials, and verify the integration contract:
|
|
2890
2897
|
|
|
2891
|
-
${e.map(o=>{let n=Qe(o.status);return`- ${o.flag}=${o.provider} ${n} \u2014 ${
|
|
2898
|
+
${e.map(o=>{let n=Qe(o.status);return`- ${o.flag}=${o.provider} ${n} \u2014 ${ce(o.status)}`}).join(`
|
|
2892
2899
|
`)}
|
|
2893
2900
|
|
|
2894
2901
|
Review the package readiness matrix before handoff: [docs/package-status.md](docs/package-status.md)
|
|
2895
|
-
`}function
|
|
2902
|
+
`}function Ea(e,t,o=[],n={}){return`# Next steps \u2014 ${e}
|
|
2896
2903
|
|
|
2897
2904
|
Region: ${t}
|
|
2898
2905
|
|
|
@@ -2918,17 +2925,17 @@ Your AI-native SaaS scaffold is ready. Complete these four steps to go from temp
|
|
|
2918
2925
|
\`\`\`
|
|
2919
2926
|
|
|
2920
2927
|
After setup, delete the welcome route at \`apps/web/src/app/[locale]/welcome/\`.
|
|
2921
|
-
${
|
|
2928
|
+
${Sa(n)}${ha(o)}
|
|
2922
2929
|
|
|
2923
2930
|
## Resources
|
|
2924
2931
|
|
|
2925
2932
|
- [Documentation](/docs)
|
|
2926
2933
|
- [GitHub](https://github.com/nebutra/nebutra-sailor)
|
|
2927
2934
|
- [Get free license (Individual/OPC)](https://nebutra.com/get-license)
|
|
2928
|
-
`}async function
|
|
2929
|
-
`)}function
|
|
2935
|
+
`}async function Qo(e,t){let{projectName:o,region:n,previewSelections:r=[],waveFeatures:s={}}=t;try{let i=Et.join(e,".sailor");Le.existsSync(i)||Le.mkdirSync(i,{recursive:!0}),Le.writeFileSync(Et.join(i,"next-steps.md"),Ea(o,n,r,s))}catch(i){let c=i instanceof Error?i.message:String(i);console.warn(`[create-sailor] Failed to write .sailor/next-steps.md: ${c}`)}try{let i=Et.join(e,"apps","web");if(!Le.existsSync(i))return;let c=Et.join(i,"src","app","[locale]","welcome");Le.existsSync(c)||Le.mkdirSync(c,{recursive:!0}),Le.writeFileSync(Et.join(c,"page.tsx"),ya(o))}catch(i){let c=i instanceof Error?i.message:String(i);console.warn(`[create-sailor] Failed to write welcome page: ${c}`)}}var Zo="create-sailor";function p(e,t){e&&process.stdout.write(JSON.stringify(t)+`
|
|
2936
|
+
`)}function ba(){let e=process.env.npm_config_user_agent??"";return e.startsWith("pnpm")?"pnpm":e.startsWith("yarn")?"yarn":e.startsWith("bun")?"bun":"npm"}function er(e){switch(e){case"postgres":case"postgresql":return"postgresql";case"mysql":return"mysql";case"sqlite":return"sqlite";case"none":return"none";default:return"postgresql"}}function tr(e,t,o){let n=e?.trim().toLowerCase()??jn(t),r=Ot(n);if(!r){let i=Zt.map(c=>c.id).join(", ");throw new Error(`Unknown --db-host="${n}". Valid: ${i}`)}let s=o;return r.forcedEngine&&s!=="none"&&s!==r.forcedEngine&&(s=r.forcedEngine),{hostId:r.id,engine:s}}function nr(e){return e==="drizzle"?"drizzle":"prisma"}function or(e){return e==="lemon"||e==="lemonsqueezy"?"lemon":e==="wechat"?"wechat":e==="alipay"?"alipay":e==="none"?"none":"stripe"}function rr(e){if(!e)return"stripe";let t=e.toLowerCase();return t==="lemon"||t==="lemonsqueezy"?"lemon":t==="wechat"?"wechat":t==="alipay"?"alipay":t==="none"?"none":"stripe"}function sr(e){if(!e)return"clerk";let t=e.toLowerCase();return t==="betterauth"||t==="better-auth"?"betterauth":t==="nextauth"||t==="next-auth"||t==="authjs"||t==="auth.js"?"nextauth":t==="none"?"none":"clerk"}var Pa=new Set(["gateway","direct","custom","none"]);function ur(e){if(!e)return!1;let t=e.trim().toLowerCase();return Pa.has(t)}function ir(e){if(!e)return["openai"];if(ur(e))return[];let t=e.split(",").map(o=>o.trim().toLowerCase());return t.includes("none")?[]:t}function Ca(e,t){let o=e??t;return pn(o)??o}var ar={mintlify:"Mintlify",docusaurus:"Docusaurus",nextra:"Nextra",vitepress:"VitePress"};function cr(e,t){if(!e)return"fumadocs";let o=e.toLowerCase();if(o==="fumadocs"||o==="none")return o;if(o in ar){let n=ar[o];return t?p(!0,{event:"notice",kind:"docs-fallback",requested:o,effective:"fumadocs"}):process.stdout.write(C.yellow(`\u26A0 ${n} support is coming in v1.2. Falling back to fumadocs.
|
|
2930
2937
|
`)+C.dim(` Track progress: https://github.com/Nebutra/Nebutra-Sailor/issues
|
|
2931
|
-
`)),"fumadocs"}return"fumadocs"}function
|
|
2938
|
+
`)),"fumadocs"}return"fumadocs"}function pr(e){switch(e){case"vercel":return"vercel";case"railway":return"railway";case"cloudflare":return"cloudflare";case"selfhost":return"selfhost";case"none":return"none";default:return"vercel"}}function lr(e){if(!e)return"global";let t=e.toLowerCase();return t==="cn"?"cn":t==="hybrid"?"hybrid":"global"}function Ia(e){return{...e==="cn"?{email:"aliyun-dm",storage:"aliyun-oss",monitoring:"sentry",analytics:"baidu",sms:"aliyun-sms"}:e==="hybrid"?{email:"resend",storage:"aliyun-oss",monitoring:"sentry",analytics:"posthog",sms:"aliyun-sms"}:{email:"resend",storage:"r2",monitoring:"sentry",analytics:"posthog",sms:"twilio"},queue:"none",search:"none",cache:e==="global"?"upstash-redis":e==="cn"?"redis":"upstash-redis",notifications:"none",webhooks:"none",cms:"none",featureFlags:"none",captcha:e==="cn"?"aliyun-slide":"turnstile",mcp:"on",metering:"auto",billingMode:"usage",idp:"clerk"}}function dr(e){return e==="cn"?"wechat":"stripe"}async function Aa(){let e=new va;e.name(Zo).description("Nebutra-Sailor \u2014 AI-Native SaaS template").version(ae,"-v, --version").helpOption(!1).argument("[name]","project directory",void 0).option("-p, --pm <id>","npm | pnpm | yarn | bun").option("--region <id>","global | cn | hybrid").option("--orm <id>","prisma (default, primary) | drizzle (dual-ORM: adds db-drizzle alongside Prisma \u2014 Postgres only)").option("--db <id>","postgres | mysql | sqlite | none \u2014 engine (Prisma provider)").option("--db-host <id>","local | supabase | neon | vercel-postgres | planetscale | railway | aliyun-rds | tencent-cdb | none \u2014 managed-provider (region default: supabase global, local cn)").option("--auth <id>","clerk | betterauth | nextauth | none").option("--social-login <ids>","CN social login providers \u2014 wechat | qq | dingtalk | workweixin | feishu | weibo (comma-separated)").option("--payment <id>","stripe | lemon | wechat | alipay | none").option("--ai <mode-or-ids>","topology shorthand (gateway | direct | custom | none) OR comma-separated provider ids (e.g. openai,anthropic)").option("--deploy <target>","vercel | railway | cloudflare | selfhost").option("--docs <id>","fumadocs | mintlify | docusaurus | nextra | vitepress | none").option("--email <id>","resend | postmark | ses | aliyun-dm | tencent-ses | netease | none").option("--storage <id>","r2 | s3 | supabase-storage | aliyun-oss | tencent-cos | qiniu | none").option("--monitoring <id>","sentry | datadog | aliyun-arms | tingyun | none").option("--analytics <id>","posthog | plausible | umami | baidu | sensors | none").option("--sms <id>","twilio | aliyun-sms | tencent-sms | yunpian | none").option("--queue <id>","qstash | bullmq | sqs | none").option("--search <id>","meilisearch | typesense | algolia | pgvector | none").option("--cache <id>","upstash-redis | vercel-kv | redis | dragonfly | none").option("--notifications <id>","novu | knock | custom | none").option("--webhooks <id>","svix | custom | none").option("--cms <id>","sanity | contentful | strapi | none").option("--feature-flags <id>","vercel-flags | growthbook | configcat | none").option("--captcha <id>","turnstile | hcaptcha | aliyun-slide | none").option("--mcp <mode>","on | off (default: on)").option("--metering <mode>","auto | on | off (default: auto \u2014 auto-on when payment is set)").option("--billing-mode <mode>","usage | seat | credits (default: usage)").option("--idp <id>","clerk | oauth-server (default: clerk)").option("--cron-jobs <bool>","true | false \u2014 scaffold scheduled cron handlers (default: true)").option("--audit-log <bool>","true | false \u2014 enable /settings/audit-log + arch test (default: true)").option("--api-keys <bool>","true | false \u2014 enable /settings/api-keys page (default: true)").option("--command-palette <bool>","true | false \u2014 enable \u2318K command palette (default: true)").option("--cookie-consent <bool>","true | false \u2014 enable GDPR/CCPA cookie banner (default: true)").option("--legal-pages <bool>","true | false \u2014 enable dynamic /legal/[slug] route (default: true)").option("--china-compliance <bool>","true | false \u2014 enable @nebutra/china-compliance + ICP footer (default: true when --region=cn, otherwise false)").option("--i18n","enable i18n").option("--no-i18n","disable i18n").option("--no-install","skip package install").option("--no-git","skip git init").option("-y, --yes","accept all defaults (non-interactive)").option("--dry-run","preview actions without writing files").option("--json","machine-readable output").option("--no-color","disable color output").option("-h, --help","show help"),e.parse(process.argv);let t=e.opts(),[o]=e.args;t.help&&(Sn(),process.exit(0));let n=!!t.json,r=!!t.dryRun,s=!!t.yes,i=s||!process.stdin.isTTY;if(n||gn(),p(n,{event:"start",version:ae}),!t.pm)try{vt("pnpm --version",{stdio:"ignore"})}catch{n||process.stderr.write(`
|
|
2932
2939
|
${C.red("\u2718")} ${C.bold("pnpm is required")} but was not found on PATH.
|
|
2933
2940
|
|
|
2934
2941
|
The scaffold uses pnpm workspaces + Turborepo. Install pnpm first:
|
|
@@ -2937,23 +2944,23 @@ The scaffold uses pnpm workspaces + Turborepo. Install pnpm first:
|
|
|
2937
2944
|
Then retry: ${C.cyan("pnpm dlx create-sailor@latest")}
|
|
2938
2945
|
(or pass ${C.dim("--pm=npm")} if you know workspace:* won't resolve in your setup)
|
|
2939
2946
|
|
|
2940
|
-
`),p(n,{event:"error",code:"PNPM_MISSING"}),process.exit(1)}let c=o??(
|
|
2941
|
-
`)),process.exit(130)}});a=String(g.name)}let l=
|
|
2942
|
-
`)),process.exit(130)}}):{};h=
|
|
2943
|
-
`))}}let
|
|
2947
|
+
`),p(n,{event:"error",code:"PNPM_MISSING"}),process.exit(1)}let c=o??(s?"./my-saas-app":void 0),a;if(c)a=c;else if(i)a="./my-saas-app";else{let g=await x.group({name:()=>x.text({message:"Where should we create your project?",placeholder:"./my-saas-app",defaultValue:"./my-saas-app",validate:E=>{if(E.length===0)return"Please enter a path."}})},{onCancel:()=>{process.stdout.write(C.red(`\u2718 Cancelled
|
|
2948
|
+
`)),process.exit(130)}});a=String(g.name)}let l=_t.basename(_t.resolve(a)),m=t.pm??ba(),S=!!t.region,T=!!t.auth,N=!!t.payment,D=!!t.ai,ie=!!t.email,b=!!t.storage,u=!!t.monitoring,U=!!t.analytics,bt=!!t.sms,dn=t.i18n!==void 0,h,Me,$,Pt="local",it,Y,F,X=An(t.socialLogin),at,$t,ye,De,ne,q,Ct;if(i){h=lr(t.region),Me=nr(t.orm);{let oe=er(t.db),y=tr(t.dbHost,h,oe);$=y.engine,Pt=y.hostId}let g=N?t.payment:dr(h);it=or(g),Y=rr(g),F=sr(t.auth);let E="gateway";if(D){let oe=t.ai?.trim().toLowerCase()??"";ur(oe)?E=oe:E="direct"}let O=Wt({mode:E,providerIds:D?ir(t.ai):void 0});at=O.mode,$t=O.routing,ye=O.providerIds,ne=pr(t.deploy),q=cr(t.docs,n),Ct=dn?!!t.i18n:!0}else{let g={};S||(g.region=()=>x.select({message:"Target region?",options:[{value:"global",label:"global \u2014 \u6D77\u5916\u4F18\u5148"},{value:"cn",label:"cn \u2014 \u56FD\u5185\u4F18\u5148"},{value:"hybrid",label:"hybrid \u2014 \u53CC\u8F68\uFF08\u56FD\u5185+\u51FA\u6D77\uFF09"}],initialValue:"global"})),T||(g.auth=()=>x.select({message:"Auth provider?",options:[{value:"clerk",label:"Clerk",hint:"managed, fastest setup"},{value:"betterauth",label:"Better Auth",hint:"self-hosted, modern (2FA / passkeys / RBAC)"},{value:"nextauth",label:"NextAuth (Auth.js v5)",hint:"self-hosted, mature, large ecosystem"},{value:"none",label:"None"}],initialValue:"clerk"})),D||(g.aiMode=()=>x.select({message:"AI topology?",options:[{value:"gateway",label:"Multi-provider AI Gateway / router",hint:"recommended"},{value:"direct",label:"Direct SDK/provider adapters"},{value:"custom",label:"OpenAI-compatible endpoint"},{value:"none",label:"Skip AI"}],initialValue:"gateway"}),g.aiProviders=({results:v})=>{if(v.aiMode!=="direct")return Promise.resolve([]);let K=Ht.map(qt=>({value:qt.id,label:`[${qt.category}] ${qt.name}`}));return x.multiselect({message:"Select direct AI provider adapters (expert path)",options:K,initialValues:["openai"],required:!1})},g.customAiName=({results:v})=>v.aiMode==="custom"?x.text({message:"Custom endpoint name (e.g. proxy, local):",defaultValue:"custom",placeholder:"custom"}):Promise.resolve(void 0),g.customAiBaseUrl=({results:v})=>v.aiMode==="custom"?x.text({message:"Custom endpoint base URL (e.g. https://api.proxy.com/v1):",validate:K=>{if(K.length===0)return"Base URL is required."}}):Promise.resolve(void 0),g.customAiApiKeyEnv=({results:v})=>v.aiMode==="custom"?x.text({message:"Environment variable name for the API Key:",defaultValue:"CUSTOM_AI_API_KEY",placeholder:"CUSTOM_AI_API_KEY"}):Promise.resolve(void 0));let E=Object.keys(g).length>0?await x.group(g,{onCancel:()=>{process.stdout.write(C.red(`\u2718 Cancelled
|
|
2949
|
+
`)),process.exit(130)}}):{};h=lr(S?t.region:E.region),Me=nr(t.orm);{let v=er(t.db),K=tr(t.dbHost,h,v);$=K.engine,Pt=K.hostId}let O=N?t.payment:dr(h);it=or(O),Y=rr(O),F=sr(T?t.auth:E.auth);let oe=E.aiMode==="custom"?{name:E.customAiName??"custom",baseURL:E.customAiBaseUrl??"",apiKeyEnvName:E.customAiApiKeyEnv??"CUSTOM_AI_API_KEY"}:void 0,y=Wt({mode:D?t.ai?.trim().toLowerCase()==="none"?"none":"direct":E.aiMode??"gateway",providerIds:D?ir(t.ai):E.aiProviders,customEndpoint:oe});at=y.mode,$t=y.routing,ye=y.providerIds,y.customEndpoint&&(De={name:y.customEndpoint.name,baseURL:y.customEndpoint.baseURL,apiKeyEnvName:y.customEndpoint.apiKeyEnvName}),ne=pr(t.deploy),q=cr(t.docs,n),Ct=dn?!!t.i18n:!0}let w=Ia(h),Fe=ie?t.email:w.email,je=Ca(b?t.storage:void 0,w.storage),Ve=u?t.monitoring:w.monitoring,Ke=U?t.analytics:w.analytics,Be=bt?t.sms:w.sms,$e=t.queue??w.queue,Ye=t.search??w.search,ct=t.cache??w.cache,qe=t.notifications??w.notifications,Ge=t.webhooks??w.webhooks,pt=t.cms??w.cms,He=t.featureFlags??w.featureFlags,We=t.captcha??w.captcha,It=t.mcp??w.mcp,lt=t.metering??w.metering,Xe=t.billingMode??w.billingMode,ze=t.idp??w.idp,I=Xo({cronJobs:t.cronJobs,auditLog:t.auditLog,apiKeys:t.apiKeys,commandPalette:t.commandPalette,cookieConsent:t.cookieConsent,legalPages:t.legalPages,chinaCompliance:t.chinaCompliance},h),Je=fn([{flag:"queue",provider:$e},{flag:"search",provider:Ye},{flag:"notifications",provider:qe},{flag:"webhooks",provider:Ge},{flag:"feature-flags",provider:He},{flag:"captcha",provider:We}]);function Yt(){for(let g of Je)if(n)p(!0,{event:"warn",step:g.flag,provider:g.provider,packageStatus:g.status,message:ce(g.status)});else{let E=Qe(g.status);process.stdout.write(C.yellow(`\u26A0 ${g.flag}=${g.provider} ${E} \u2014 ${ce(g.status)}
|
|
2950
|
+
`))}}let Se={region:h,orm:Me,database:$,payment:it,aiMode:at,aiRouting:$t,aiProviders:ye,customAiEndpoint:De,deployTarget:ne,docs:q,i18n:Ct,email:Fe,storage:je,monitoring:Ve,analytics:Ke,sms:Be,queue:$e,search:Ye,cache:ct,notifications:qe,webhooks:Ge,cms:pt,featureFlags:He,captcha:We,mcp:It,metering:lt,billingMode:Xe,idp:ze,cronJobs:I.cronJobs,auditLog:I.auditLog,apiKeys:I.apiKeys,commandPalette:I.commandPalette,cookieConsent:I.cookieConsent,legalPages:I.legalPages,chinaCompliance:I.chinaCompliance},At=[["Project name",l],["Region",h],["Auth",F],["Social login",X.length>0?X.map(g=>dt.find(E=>E.id===g)?.name??g).join(", "):"none"],["ORM",Me],["Database",$],["Payment",Y],["AI topology",`${at}${ye.length>0?` (${ye.join(", ")} seed)`:""}${De?" + custom endpoint":""}`],["Email",Fe],["Storage",je],["Monitoring",Ve],["Analytics",Ke],["SMS",Be],["Deploy Target",ne],["Docs Framework",q]];n?At.forEach(([g,E],O)=>{p(!0,{event:"step",step:g,value:E,index:O+1,total:At.length})}):At.forEach(([g,E],O)=>{En({index:O+1,total:At.length,label:g,value:E})});let mr={databaseUrl:"postgresql://postgres:postgres@localhost:5432/nebutra",clerkPublishable:"",clerkSecret:""},un=Date.now();if(r){let g=ye.length,E=[`clone template \u2192 ${a}`,"write nebutra.config.json",`prune template (orm=${Me}, i18n=${Ct})`,`region \u2192 ${h}`,`auth \u2192 ${F==="none"?"skip (remove packages/auth)":`configure ${F}`}`,...X.length>0?[`social-login \u2192 generate ${X.length} callback route${X.length===1?"":"s"} + SocialLoginButtons.tsx (${X.join(", ")})`]:[],`db \u2192 ${$==="none"?"skip (remove packages/db)":`configure Prisma for ${$}`}`,`payment \u2192 ${Y==="none"?"skip (remove packages/billing)":`configure ${Y}`}`,...q!=="none"?[`docs \u2192 scaffold apps/docs (${q==="fumadocs"?"fumadocs":`${q} \u2192 fumadocs fallback`})`]:[],...g>0||De?[`ai-providers \u2192 generate ${at} registry seed + env (${g}${De?" + custom":""} provider${g===1&&!De?"":"s"})`]:[],...Fe!=="none"?[`email \u2192 configure ${Fe}`]:[],...je!=="none"?[`storage \u2192 configure ${je}`]:[],...Ve!=="none"?[`monitoring \u2192 configure ${Ve}`]:[],...Ke!=="none"?[`analytics \u2192 configure ${Ke}`]:[],...Be!=="none"?[`sms \u2192 configure ${Be}`]:[],...$e!=="none"?[`queue \u2192 configure ${$e}`]:[],...Ye!=="none"?[`search \u2192 configure ${Ye}`]:[],...ct!=="none"?[`cache \u2192 configure ${ct}`]:[],...qe!=="none"?[`notifications \u2192 configure ${qe}`]:[],...Ge!=="none"?[`webhooks \u2192 configure ${Ge}`]:[],...pt!=="none"?[`cms \u2192 configure ${pt}`]:[],...He!=="none"?[`feature-flags \u2192 configure ${He}`]:[],...We!=="none"?[`captcha \u2192 configure ${We}`]:[],`mcp \u2192 ${It==="on"?"enable MCP server":"remove packages/mcp"}`,`metering \u2192 ${lt==="off"?"disabled":lt==="auto"?it!=="none"?"enabled (auto: payment set)":"disabled (auto: no payment)":"enabled"}`,...Xe!=="usage"?[`billing-mode \u2192 ${Xe}`]:[],...ze!=="clerk"?[`idp \u2192 ${ze}`]:[],`cron-jobs \u2192 ${I.cronJobs?"enabled":"disabled"}`,`audit-log \u2192 ${I.auditLog?"enabled":"disabled"}`,`api-keys \u2192 ${I.apiKeys?"enabled":"disabled"}`,`command-palette \u2192 ${I.commandPalette?"enabled":"disabled"}`,`cookie-consent \u2192 ${I.cookieConsent?"enabled":"disabled"}`,`legal-pages \u2192 ${I.legalPages?"enabled":"disabled"}`,`china-compliance \u2192 ${I.chinaCompliance?"enabled (@nebutra/china-compliance + ICP footer)":"disabled"}`,`compliance \u2192 inject ${h} boilerplate (ICP/Cookie/AIGC/Privacy)`,"welcome \u2192 generate dev welcome page","env \u2192 generate random secrets (AUTH_SECRET, JWT_SECRET)","seed \u2192 generate prisma/seed.ts (1 admin + 3 tenants)",...ne!=="none"?[`deploy \u2192 inject ${ne} config`]:[],"inject .env.local",t.install===!1?"skip install":`run ${m} install`,t.git===!1?"skip git init":"run git init"];if(n){for(let O of E)p(!0,{event:"plan",action:O});Yt(),p(!0,{event:"done",dryRun:!0})}else{process.stdout.write(`
|
|
2944
2951
|
`+C.bold(`Dry run \u2014 planned actions:
|
|
2945
|
-
`));for(let
|
|
2952
|
+
`));for(let O of E)process.stdout.write(` \u2022 ${O}
|
|
2946
2953
|
`);Je.length>0&&(process.stdout.write(`
|
|
2947
2954
|
`+C.bold(C.yellow(`Preview-status providers selected:
|
|
2948
|
-
`))),
|
|
2955
|
+
`))),Yt()),process.stdout.write(C.dim(`
|
|
2949
2956
|
No files were written.
|
|
2950
|
-
`))}process.exit(0)}let
|
|
2957
|
+
`))}process.exit(0)}let gr=async()=>{process.stdout.write(`
|
|
2951
2958
|
`+C.red(`\u2718 Cancelled
|
|
2952
|
-
`)),
|
|
2953
|
-
`))),process.exit(130)};process.on("SIGINT",
|
|
2954
|
-
`));try{
|
|
2955
|
-
`))}}else p(n,{event:"step",step:"install",status:"skip"});if(t.git!==!1){n&&p(!0,{event:"step",step:"git-init",status:"start"});try{
|
|
2956
|
-
`))}}else p(n,{event:"step",step:"git-init",status:"skip"});let
|
|
2957
|
-
\u2718 Failed: ${
|
|
2958
|
-
`)),process.exit(1)}}
|
|
2959
|
+
`)),fe.existsSync(a)&&await x.confirm({message:`Cleanup partial install at ${a}?`,initialValue:!0})===!0&&(fe.rmSync(a,{recursive:!0,force:!0}),process.stdout.write(C.dim(` \u2713 Removed ${a}
|
|
2960
|
+
`))),process.exit(130)};process.on("SIGINT",gr);try{p(n,{event:"step",step:"clone",status:"start"}),await mo(a),p(n,{event:"step",step:"clone",status:"ok"}),p(n,{event:"step",step:"package",status:"start"}),await bo(a,l),p(n,{event:"step",step:"package",status:"ok"}),p(n,{event:"step",step:"config",status:"start"}),await Fn(a,Se),p(n,{event:"step",step:"config",status:"ok"}),p(n,{event:"step",step:"prune",status:"start"}),await Uo(a,Se),p(n,{event:"step",step:"prune",status:"ok"});let g=_t.join(a,"packages/platform/db/prisma/schema.prisma");if(fe.existsSync(g)){p(n,{event:"step",step:"schema-prune",status:"start"});try{let y=fe.readFileSync(g,"utf-8"),v=an(y,{auth:F,payment:Y,"billing-mode":Xe,idp:ze,template:"saas"});fe.writeFileSync(g,v),p(n,{event:"step",step:"schema-prune",status:"ok"})}catch(y){let v=y instanceof Error?y.message:String(y);p(n,{event:"step",step:"schema-prune",status:"error",error:v})}}else p(n,{event:"step",step:"schema-prune",status:"skip"});if(p(n,{event:"step",step:"auth",choice:F,status:"start"}),await Cn(a,F),p(n,{event:"step",step:"auth",choice:F,status:"ok"}),X.length>0?(p(n,{event:"step",step:"social-login",providers:X,status:"start"}),await Rn(a,X,F),p(n,{event:"step",step:"social-login",providers:X,status:"ok"})):p(n,{event:"step",step:"social-login",status:"skip"}),p(n,{event:"step",step:"db",choice:$,status:"start"}),await Bn(a,$,l),$!=="none"){let y=Ot(Pt);y&&(await $n(a,y,l),p(n,{event:"step",step:"db-host",choice:Pt,status:"ok"}))}if(p(n,{event:"step",step:"db",choice:$,status:"ok"}),Me==="drizzle"){p(n,{event:"step",step:"orm",choice:"drizzle",status:"start"});let y=await Io(a,"drizzle",$);p(n,{event:"step",step:"orm",choice:"drizzle",status:y.applied?"ok":"skip",...y.reason?{reason:y.reason}:{}})}if(p(n,{event:"step",step:"payment",choice:Y,status:"start"}),await wo(a,Y),p(n,{event:"step",step:"payment",choice:Y,status:"ok"}),await Qn(a,Fe,h),n&&p(!0,{event:"step",step:"email",choice:Fe,status:"ok"}),await Wo(a,je,h),n&&p(!0,{event:"step",step:"storage",choice:je,status:"ok"}),await Eo(a,Ve,h),n&&p(!0,{event:"step",step:"monitoring",choice:Ve,status:"ok"}),await bn(a,Ke,h),n&&p(!0,{event:"step",step:"analytics",choice:Ke,status:"ok"}),await Go(a,Be,h),n&&p(!0,{event:"step",step:"sms",choice:Be,status:"ok"}),await jo(a,$e,h),n&&p(!0,{event:"step",step:"queue",choice:$e,status:"ok"}),await $o(a,Ye,h),n&&p(!0,{event:"step",step:"search",choice:Ye,status:"ok"}),await Nn(a,ct,h),n&&p(!0,{event:"step",step:"cache",choice:ct,status:"ok"}),await _o(a,qe,h),n&&p(!0,{event:"step",step:"notifications",choice:qe,status:"ok"}),await Jo(a,Ge,h),n&&p(!0,{event:"step",step:"webhooks",choice:Ge,status:"ok"}),await Mn(a,pt,h),n&&p(!0,{event:"step",step:"cms",choice:pt,status:"ok"}),await so(a,He,h),n&&p(!0,{event:"step",step:"feature-flags",choice:He,status:"ok"}),await Un(a,We,h),n&&p(!0,{event:"step",step:"captcha",choice:We,status:"ok"}),await go(a,It),n&&p(!0,{event:"step",step:"mcp",mode:It,status:"ok"}),await yo(a,lt,it),n&&p(!0,{event:"step",step:"metering",mode:lt,status:"ok"}),p(n,{event:"step",step:"prune-wave-features",status:"start"}),Lo(a,I),p(n,{event:"step",step:"prune-wave-features",status:"ok",toggles:I}),Se.orm==="prisma"&&Se.database!=="none"){let y=_t.join(a,"packages/platform/db/prisma/schema.prisma");if(fe.existsSync(y)){p(n,{event:"step",step:"schema-prune",status:"start"});try{let v=fe.readFileSync(y,"utf8"),K=an(v,{auth:F,payment:Y,"billing-mode":Xe,idp:ze,template:"saas"});fe.writeFileSync(y,K),p(n,{event:"step",step:"schema-prune",status:"ok"})}catch(v){let K=v instanceof Error?v.message:String(v);p(n,{event:"step",step:"schema-prune",status:"error",error:K})}}else p(n,{event:"step",step:"schema-prune",status:"skip"})}else p(n,{event:"step",step:"schema-prune",status:"skip"});if(p(n,{event:"step",step:"billing-mode",choice:Xe,status:"ok"}),p(n,{event:"step",step:"idp",choice:ze,status:"ok"}),await Dn(a,h),n&&p(!0,{event:"step",step:"compliance",region:h,status:"ok"}),await oo(a),await Yo(a,F),await Qo(a,{projectName:l,region:h,previewSelections:Je,waveFeatures:I}),Se.aiMode!=="none"){p(n,{event:"step",step:"ai-providers",status:"start"});let y={providerIds:Se.aiProviders,customEndpoint:Se.customAiEndpoint},v=_t.join(a,"packages/ai/ai-providers/templates");await No(a,y,v),p(n,{event:"step",step:"ai-providers",status:"ok"})}else p(n,{event:"step",step:"ai-providers",status:"skip"});if(p(n,{event:"step",step:"docs",status:"start"}),q!=="none"?(await Wn(a,{framework:q,projectName:l}),p(n,{event:"step",step:"docs",framework:"fumadocs",requested:q,status:"ok"})):p(n,{event:"step",step:"docs",status:"skip"}),p(n,{event:"step",step:"deploy-target",status:"start"}),ne!=="none"&&await qn(a,ne),p(n,{event:"step",step:"deploy-target",status:"ok"}),p(n,{event:"step",step:"env",status:"start"}),await eo(a,mr),p(n,{event:"step",step:"env",status:"ok"}),t.install!==!1){let y=m==="bun"?"bun install":`${m} install`;n?p(!0,{event:"step",step:"install",pm:m,status:"start"}):process.stdout.write(C.dim(` Installing dependencies with ${m}\u2026
|
|
2961
|
+
`));try{vt(y,{cwd:a,stdio:n?"ignore":"inherit"}),p(n,{event:"step",step:"install",status:"ok"})}catch(v){let K=v instanceof Error?v.message:String(v);n?p(!0,{event:"step",step:"install",status:"error",error:K}):process.stdout.write(C.yellow(` \u26A0 install failed \u2014 run '${y}' manually.
|
|
2962
|
+
`))}}else p(n,{event:"step",step:"install",status:"skip"});if(t.git!==!1){n&&p(!0,{event:"step",step:"git-init",status:"start"});try{vt("git init -q",{cwd:a,stdio:"ignore"}),vt("git add -A",{cwd:a,stdio:"ignore"}),vt('git -c user.email=you@example.com -c user.name="You" commit -q -m "chore: initial scaffold from create-sailor"',{cwd:a,stdio:"ignore"}),p(n,{event:"step",step:"git-init",status:"ok"})}catch(y){let v=y instanceof Error?y.message:String(y);n?p(!0,{event:"step",step:"git-init",status:"error",error:v}):process.stdout.write(C.yellow(` \u26A0 git init skipped \u2014 not fatal.
|
|
2963
|
+
`))}}else p(n,{event:"step",step:"git-init",status:"skip"});let oe=Math.max(1,Math.round((Date.now()-un)/1e3));Pn({template_version:ae,package_manager:m,region:h,auth:F,payment:Y,ai_providers:ye,deploy_target:ne,duration_ms:Date.now()-un}),Je.length>0&&Yt(),n?p(!0,{event:"done",status:"ok",elapsedSec:oe,targetDir:a,previewSelections:Je,waveFeatures:I}):yn({elapsedSec:oe,targetDir:a,skippedInstall:t.install===!1,previewSelections:Je,waveFeatures:I});try{_a({pkg:{name:Zo,version:ae},updateCheckInterval:1e3*60*60*24}).notify({defer:!1,isGlobal:!0})}catch{}process.exit(0)}catch(g){let E=g instanceof Error?g.message:String(g);n?p(!0,{event:"error",message:E}):process.stdout.write(C.red(`
|
|
2964
|
+
\u2718 Failed: ${E}
|
|
2965
|
+
`)),process.exit(1)}}Aa().catch(e=>{process.stderr.write(String(e)+`
|
|
2959
2966
|
`),process.exit(1)});
|