@musashishao/agent-kit 1.8.1 → 1.9.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/.agent/agents/ai-architect.md +39 -0
- package/.agent/agents/cloud-engineer.md +39 -0
- package/.agent/agents/game-asset-curator.md +317 -0
- package/.agent/agents/game-developer.md +190 -89
- package/.agent/agents/game-narrative-designer.md +310 -0
- package/.agent/agents/game-qa-agent.md +441 -0
- package/.agent/agents/marketing-specialist.md +41 -0
- package/.agent/agents/penetration-tester.md +15 -1
- package/.agent/rules/CODEX.md +26 -2
- package/.agent/rules/GEMINI.md +7 -5
- package/.agent/rules/REFERENCE.md +92 -2
- package/.agent/scripts/ak_cli.py +1 -1
- package/.agent/scripts/localize_workflows.py +54 -0
- package/.agent/scripts/memory_manager.py +24 -1
- package/.agent/skills/3d-web-experience/SKILL.md +386 -0
- package/.agent/skills/DEPENDENCIES.md +54 -0
- package/.agent/skills/ab-test-setup/SKILL.md +77 -0
- package/.agent/skills/active-directory-attacks/SKILL.md +59 -0
- package/.agent/skills/agent-evaluation/SKILL.md +430 -0
- package/.agent/skills/agent-memory-systems/SKILL.md +426 -0
- package/.agent/skills/agent-tool-builder/SKILL.md +139 -0
- package/.agent/skills/ai-agents-architect/SKILL.md +115 -0
- package/.agent/skills/ai-product/SKILL.md +86 -0
- package/.agent/skills/ai-wrapper-product/SKILL.md +90 -0
- package/.agent/skills/analytics-tracking/SKILL.md +88 -0
- package/.agent/skills/api-fuzzing-bug-bounty/SKILL.md +66 -0
- package/.agent/skills/app-store-optimization/SKILL.md +66 -0
- package/.agent/skills/autonomous-agent-patterns/SKILL.md +414 -0
- package/.agent/skills/aws-penetration-testing/SKILL.md +50 -0
- package/.agent/skills/aws-serverless/SKILL.md +327 -0
- package/.agent/skills/azure-functions/SKILL.md +340 -0
- package/.agent/skills/broken-authentication/SKILL.md +53 -0
- package/.agent/skills/browser-automation/SKILL.md +408 -0
- package/.agent/skills/browser-extension-builder/SKILL.md +422 -0
- package/.agent/skills/bullmq-specialist/SKILL.md +424 -0
- package/.agent/skills/bun-development/SKILL.md +386 -0
- package/.agent/skills/burp-suite-testing/SKILL.md +60 -0
- package/.agent/skills/clerk-auth/SKILL.md +432 -0
- package/.agent/skills/cloud-penetration-testing/SKILL.md +51 -0
- package/.agent/skills/copywriting/SKILL.md +66 -0
- package/.agent/skills/crewai/SKILL.md +470 -0
- package/.agent/skills/discord-bot-architect/SKILL.md +447 -0
- package/.agent/skills/email-sequence/SKILL.md +73 -0
- package/.agent/skills/ethical-hacking-methodology/SKILL.md +67 -0
- package/.agent/skills/firebase/SKILL.md +377 -0
- package/.agent/skills/game-development/godot-expert/SKILL.md +462 -0
- package/.agent/skills/game-development/npc-ai-integration/SKILL.md +110 -0
- package/.agent/skills/game-development/procedural-generation/SKILL.md +168 -0
- package/.agent/skills/game-development/unity-integration/SKILL.md +358 -0
- package/.agent/skills/game-development/webgpu-shading/SKILL.md +209 -0
- package/.agent/skills/gcp-cloud-run/SKILL.md +358 -0
- package/.agent/skills/graphql/SKILL.md +492 -0
- package/.agent/skills/idor-testing/SKILL.md +64 -0
- package/.agent/skills/inngest/SKILL.md +128 -0
- package/.agent/skills/langfuse/SKILL.md +415 -0
- package/.agent/skills/langgraph/SKILL.md +360 -0
- package/.agent/skills/launch-strategy/SKILL.md +68 -0
- package/.agent/skills/linux-privilege-escalation/SKILL.md +62 -0
- package/.agent/skills/llm-app-patterns/SKILL.md +367 -0
- package/.agent/skills/marketing-ideas/SKILL.md +66 -0
- package/.agent/skills/metasploit-framework/SKILL.md +60 -0
- package/.agent/skills/micro-saas-launcher/SKILL.md +93 -0
- package/.agent/skills/neon-postgres/SKILL.md +339 -0
- package/.agent/skills/paid-ads/SKILL.md +64 -0
- package/.agent/skills/supabase-integration/SKILL.md +411 -0
- package/.agent/workflows/ai-agent.md +36 -0
- package/.agent/workflows/autofix.md +1 -0
- package/.agent/workflows/brainstorm.md +1 -0
- package/.agent/workflows/context.md +1 -0
- package/.agent/workflows/create.md +1 -0
- package/.agent/workflows/dashboard.md +1 -0
- package/.agent/workflows/debug.md +1 -0
- package/.agent/workflows/deploy.md +1 -0
- package/.agent/workflows/enhance.md +1 -0
- package/.agent/workflows/game-prototype.md +154 -0
- package/.agent/workflows/marketing.md +37 -0
- package/.agent/workflows/next.md +1 -0
- package/.agent/workflows/orchestrate.md +1 -0
- package/.agent/workflows/pentest.md +37 -0
- package/.agent/workflows/plan.md +1 -0
- package/.agent/workflows/preview.md +2 -1
- package/.agent/workflows/quality.md +1 -0
- package/.agent/workflows/saas.md +36 -0
- package/.agent/workflows/spec.md +1 -0
- package/.agent/workflows/status.md +1 -0
- package/.agent/workflows/test.md +1 -0
- package/.agent/workflows/ui-ux-pro-max.md +1 -0
- package/README.md +52 -24
- package/bin/cli.js +68 -3
- package/docs/CHANGELOG_AI_INFRA.md +30 -0
- package/docs/MIGRATION_GUIDE_V1.9.md +55 -0
- package/package.json +1 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: webgpu-shading
|
|
3
|
+
description: WebGPU and shader programming for web games. Covers WGSL, Three.js WebGPU, Babylon.js, and shader optimization.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Grep, WebSearch
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# WebGPU & Shading Skill
|
|
8
|
+
|
|
9
|
+
> Next-generation web graphics with WebGPU and WGSL.
|
|
10
|
+
|
|
11
|
+
## When to Use
|
|
12
|
+
|
|
13
|
+
- Developing 3D web games with modern graphics
|
|
14
|
+
- Writing custom shaders (WGSL, GLSL)
|
|
15
|
+
- Optimizing rendering performance
|
|
16
|
+
- Using Three.js or Babylon.js with WebGPU
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## WebGPU Browser Support (2025)
|
|
21
|
+
|
|
22
|
+
| Browser | Status |
|
|
23
|
+
|---------|--------|
|
|
24
|
+
| Chrome 113+ | ✅ Stable |
|
|
25
|
+
| Edge 113+ | ✅ Stable |
|
|
26
|
+
| Firefox 131+ | ✅ Stable |
|
|
27
|
+
| Safari 18+ | ✅ Stable |
|
|
28
|
+
| **Total Coverage** | ~78% |
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## WebGPU vs WebGL
|
|
33
|
+
|
|
34
|
+
| Feature | WebGL | WebGPU |
|
|
35
|
+
|---------|-------|--------|
|
|
36
|
+
| API Style | Immediate | Command-based |
|
|
37
|
+
| Compute Shaders | No | Yes |
|
|
38
|
+
| Multi-threading | Limited | Full |
|
|
39
|
+
| Memory Control | Low | High |
|
|
40
|
+
| Performance | Good | Better |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Basic WebGPU Setup
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
async function initWebGPU() {
|
|
48
|
+
if (!navigator.gpu) {
|
|
49
|
+
throw new Error("WebGPU not supported");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const adapter = await navigator.gpu.requestAdapter();
|
|
53
|
+
const device = await adapter.requestDevice();
|
|
54
|
+
|
|
55
|
+
const canvas = document.getElementById("canvas");
|
|
56
|
+
const context = canvas.getContext("webgpu");
|
|
57
|
+
|
|
58
|
+
context.configure({
|
|
59
|
+
device,
|
|
60
|
+
format: navigator.gpu.getPreferredCanvasFormat(),
|
|
61
|
+
alphaMode: "premultiplied"
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return { device, context };
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## WGSL Shader Basics
|
|
71
|
+
|
|
72
|
+
### Vertex Shader
|
|
73
|
+
|
|
74
|
+
```wgsl
|
|
75
|
+
struct VertexInput {
|
|
76
|
+
@location(0) position: vec3f,
|
|
77
|
+
@location(1) uv: vec2f,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
struct VertexOutput {
|
|
81
|
+
@builtin(position) position: vec4f,
|
|
82
|
+
@location(0) uv: vec2f,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
@group(0) @binding(0) var<uniform> mvp: mat4x4f;
|
|
86
|
+
|
|
87
|
+
@vertex
|
|
88
|
+
fn main(input: VertexInput) -> VertexOutput {
|
|
89
|
+
var output: VertexOutput;
|
|
90
|
+
output.position = mvp * vec4f(input.position, 1.0);
|
|
91
|
+
output.uv = input.uv;
|
|
92
|
+
return output;
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Fragment Shader
|
|
97
|
+
|
|
98
|
+
```wgsl
|
|
99
|
+
@group(0) @binding(1) var textureSampler: sampler;
|
|
100
|
+
@group(0) @binding(2) var texture: texture_2d<f32>;
|
|
101
|
+
|
|
102
|
+
@fragment
|
|
103
|
+
fn main(@location(0) uv: vec2f) -> @location(0) vec4f {
|
|
104
|
+
return textureSample(texture, textureSampler, uv);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Three.js WebGPU
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
import * as THREE from 'three/webgpu';
|
|
114
|
+
import { MeshStandardNodeMaterial } from 'three/nodes';
|
|
115
|
+
|
|
116
|
+
async function createScene() {
|
|
117
|
+
const renderer = new THREE.WebGPURenderer();
|
|
118
|
+
await renderer.init();
|
|
119
|
+
|
|
120
|
+
renderer.setSize(window.innerWidth, window.innerHeight);
|
|
121
|
+
document.body.appendChild(renderer.domElement);
|
|
122
|
+
|
|
123
|
+
const scene = new THREE.Scene();
|
|
124
|
+
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight);
|
|
125
|
+
camera.position.z = 5;
|
|
126
|
+
|
|
127
|
+
// Node-based material for WebGPU
|
|
128
|
+
const material = new MeshStandardNodeMaterial({
|
|
129
|
+
color: 0x00ff00,
|
|
130
|
+
roughness: 0.5,
|
|
131
|
+
metalness: 0.5
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const geometry = new THREE.BoxGeometry();
|
|
135
|
+
const mesh = new THREE.Mesh(geometry, material);
|
|
136
|
+
scene.add(mesh);
|
|
137
|
+
|
|
138
|
+
function animate() {
|
|
139
|
+
mesh.rotation.x += 0.01;
|
|
140
|
+
mesh.rotation.y += 0.01;
|
|
141
|
+
renderer.render(scene, camera);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
renderer.setAnimationLoop(animate);
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Common Shader Effects
|
|
151
|
+
|
|
152
|
+
### Screen Flash
|
|
153
|
+
|
|
154
|
+
```wgsl
|
|
155
|
+
@group(0) @binding(0) var<uniform> flashAmount: f32;
|
|
156
|
+
@group(0) @binding(1) var<uniform> flashColor: vec3f;
|
|
157
|
+
|
|
158
|
+
@fragment
|
|
159
|
+
fn main(@location(0) uv: vec2f) -> @location(0) vec4f {
|
|
160
|
+
let texColor = textureSample(texture, sampler, uv);
|
|
161
|
+
return mix(texColor, vec4f(flashColor, 1.0), flashAmount);
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Outline Effect
|
|
166
|
+
|
|
167
|
+
```wgsl
|
|
168
|
+
fn outline(uv: vec2f, size: f32) -> f32 {
|
|
169
|
+
let offsets = array<vec2f, 4>(
|
|
170
|
+
vec2f(size, 0.0),
|
|
171
|
+
vec2f(-size, 0.0),
|
|
172
|
+
vec2f(0.0, size),
|
|
173
|
+
vec2f(0.0, -size)
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
var alpha = 0.0;
|
|
177
|
+
for (var i = 0; i < 4; i++) {
|
|
178
|
+
alpha += textureSample(texture, sampler, uv + offsets[i]).a;
|
|
179
|
+
}
|
|
180
|
+
return alpha;
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Performance Tips
|
|
187
|
+
|
|
188
|
+
| Tip | Impact |
|
|
189
|
+
|-----|--------|
|
|
190
|
+
| Batch draw calls | High |
|
|
191
|
+
| Use instancing | High |
|
|
192
|
+
| Minimize state changes | Medium |
|
|
193
|
+
| Compress textures (KTX2) | Medium |
|
|
194
|
+
| Use LOD for meshes | Medium |
|
|
195
|
+
| Avoid branching in shaders | Low |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Anti-Patterns
|
|
200
|
+
|
|
201
|
+
| ❌ Don't | ✅ Do |
|
|
202
|
+
|----------|-------|
|
|
203
|
+
| Load uncompressed textures | Use Basis Universal/KTX2 |
|
|
204
|
+
| Draw call per object | Batch/Instance |
|
|
205
|
+
| Dynamic uniforms per frame | Uniform buffer objects |
|
|
206
|
+
| Complex branching in shader | Precompute |
|
|
207
|
+
| Skip feature detection | Test `navigator.gpu` |
|
|
208
|
+
|
|
209
|
+
> **Remember:** WebGPU is the future of web graphics. Design for it, with WebGL fallback.
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gcp-cloud-run
|
|
3
|
+
description: "Google Cloud Run and Cloud Functions patterns. Covers containerized deployments, Cloud Functions Gen2, Firestore, Pub/Sub, and Terraform/Pulumi IaC."
|
|
4
|
+
version: "1.0.0"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ☁️ GCP Cloud Run
|
|
8
|
+
|
|
9
|
+
You are a Google Cloud expert who has deployed production applications on Cloud Run and Cloud Functions. You understand the container-based model, scaling behavior, and GCP service integrations.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## When to Use This Skill
|
|
14
|
+
|
|
15
|
+
- Deploying containerized apps with Cloud Run
|
|
16
|
+
- Event-driven processing with Cloud Functions Gen2
|
|
17
|
+
- Firestore/Bigtable data modeling
|
|
18
|
+
- Pub/Sub message queues
|
|
19
|
+
- Cloud Build CI/CD pipelines
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Capabilities
|
|
24
|
+
|
|
25
|
+
- `cloud-run`
|
|
26
|
+
- `cloud-functions`
|
|
27
|
+
- `firestore`
|
|
28
|
+
- `pub-sub`
|
|
29
|
+
- `cloud-build`
|
|
30
|
+
- `artifact-registry`
|
|
31
|
+
- `cloud-tasks`
|
|
32
|
+
- `cloud-scheduler`
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 1. Cloud Run Patterns
|
|
37
|
+
|
|
38
|
+
### Dockerfile for Cloud Run
|
|
39
|
+
|
|
40
|
+
```dockerfile
|
|
41
|
+
# Dockerfile
|
|
42
|
+
FROM node:20-slim AS builder
|
|
43
|
+
|
|
44
|
+
WORKDIR /app
|
|
45
|
+
COPY package*.json ./
|
|
46
|
+
RUN npm ci --only=production
|
|
47
|
+
|
|
48
|
+
COPY . .
|
|
49
|
+
RUN npm run build
|
|
50
|
+
|
|
51
|
+
# Production image
|
|
52
|
+
FROM node:20-slim
|
|
53
|
+
|
|
54
|
+
ENV NODE_ENV=production
|
|
55
|
+
WORKDIR /app
|
|
56
|
+
|
|
57
|
+
COPY --from=builder /app/dist ./dist
|
|
58
|
+
COPY --from=builder /app/node_modules ./node_modules
|
|
59
|
+
COPY package*.json ./
|
|
60
|
+
|
|
61
|
+
# Cloud Run uses PORT env var
|
|
62
|
+
EXPOSE 8080
|
|
63
|
+
CMD ["node", "dist/server.js"]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Node.js Server
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
// server.js
|
|
70
|
+
const express = require('express');
|
|
71
|
+
const { Firestore } = require('@google-cloud/firestore');
|
|
72
|
+
|
|
73
|
+
const app = express();
|
|
74
|
+
app.use(express.json());
|
|
75
|
+
|
|
76
|
+
// Initialize outside request handler
|
|
77
|
+
const firestore = new Firestore();
|
|
78
|
+
const collection = firestore.collection('users');
|
|
79
|
+
|
|
80
|
+
app.get('/users/:id', async (req, res) => {
|
|
81
|
+
try {
|
|
82
|
+
const doc = await collection.doc(req.params.id).get();
|
|
83
|
+
|
|
84
|
+
if (!doc.exists) {
|
|
85
|
+
return res.status(404).json({ error: 'User not found' });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
res.json({ id: doc.id, ...doc.data() });
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.error('Error:', error);
|
|
91
|
+
res.status(500).json({ error: error.message });
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
app.post('/users', async (req, res) => {
|
|
96
|
+
try {
|
|
97
|
+
const docRef = await collection.add(req.body);
|
|
98
|
+
res.status(201).json({ id: docRef.id });
|
|
99
|
+
} catch (error) {
|
|
100
|
+
res.status(500).json({ error: error.message });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Cloud Run provides PORT
|
|
105
|
+
const PORT = process.env.PORT || 8080;
|
|
106
|
+
app.listen(PORT, () => {
|
|
107
|
+
console.log(`Server running on port ${PORT}`);
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Deploy Command
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Build and push
|
|
115
|
+
gcloud builds submit --tag gcr.io/$PROJECT_ID/my-service
|
|
116
|
+
|
|
117
|
+
# Deploy
|
|
118
|
+
gcloud run deploy my-service \
|
|
119
|
+
--image gcr.io/$PROJECT_ID/my-service \
|
|
120
|
+
--platform managed \
|
|
121
|
+
--region us-central1 \
|
|
122
|
+
--allow-unauthenticated \
|
|
123
|
+
--min-instances 1 \
|
|
124
|
+
--max-instances 100 \
|
|
125
|
+
--memory 512Mi \
|
|
126
|
+
--cpu 1 \
|
|
127
|
+
--set-env-vars "NODE_ENV=production"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 2. Cloud Functions Gen2 Pattern
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
// index.js
|
|
136
|
+
const functions = require('@google-cloud/functions-framework');
|
|
137
|
+
const { Firestore } = require('@google-cloud/firestore');
|
|
138
|
+
|
|
139
|
+
const firestore = new Firestore();
|
|
140
|
+
|
|
141
|
+
// HTTP function
|
|
142
|
+
functions.http('helloHttp', async (req, res) => {
|
|
143
|
+
const name = req.query.name || req.body.name || 'World';
|
|
144
|
+
res.json({ message: `Hello, ${name}!` });
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Cloud Event function (Pub/Sub)
|
|
148
|
+
functions.cloudEvent('helloPubSub', async (cloudEvent) => {
|
|
149
|
+
const message = Buffer.from(cloudEvent.data.message.data, 'base64').toString();
|
|
150
|
+
console.log(`Received message: ${message}`);
|
|
151
|
+
|
|
152
|
+
// Process message
|
|
153
|
+
await firestore.collection('messages').add({
|
|
154
|
+
content: message,
|
|
155
|
+
timestamp: new Date()
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Firestore trigger
|
|
160
|
+
functions.cloudEvent('onUserCreate', async (event) => {
|
|
161
|
+
const document = event.data;
|
|
162
|
+
console.log('New user created:', document);
|
|
163
|
+
|
|
164
|
+
// Send welcome email
|
|
165
|
+
await sendWelcomeEmail(document.email);
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Deploy Cloud Function
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
gcloud functions deploy my-function \
|
|
173
|
+
--gen2 \
|
|
174
|
+
--runtime nodejs20 \
|
|
175
|
+
--trigger-http \
|
|
176
|
+
--allow-unauthenticated \
|
|
177
|
+
--region us-central1 \
|
|
178
|
+
--entry-point helloHttp
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 3. Pub/Sub Pattern
|
|
184
|
+
|
|
185
|
+
### Publisher
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
const { PubSub } = require('@google-cloud/pubsub');
|
|
189
|
+
const pubsub = new PubSub();
|
|
190
|
+
|
|
191
|
+
async function publishMessage(topicName, data) {
|
|
192
|
+
const topic = pubsub.topic(topicName);
|
|
193
|
+
|
|
194
|
+
const messageBuffer = Buffer.from(JSON.stringify(data));
|
|
195
|
+
|
|
196
|
+
const messageId = await topic.publish(messageBuffer, {
|
|
197
|
+
// Ordering key for ordered delivery
|
|
198
|
+
orderingKey: data.userId
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
console.log(`Message ${messageId} published.`);
|
|
202
|
+
return messageId;
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Subscriber (Push to Cloud Run)
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
// Pub/Sub push endpoint in Cloud Run
|
|
210
|
+
app.post('/pubsub/push', async (req, res) => {
|
|
211
|
+
try {
|
|
212
|
+
// Verify Pub/Sub token if configured
|
|
213
|
+
const token = req.query.token;
|
|
214
|
+
if (token !== process.env.PUBSUB_VERIFICATION_TOKEN) {
|
|
215
|
+
return res.status(403).send('Forbidden');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const message = JSON.parse(
|
|
219
|
+
Buffer.from(req.body.message.data, 'base64').toString()
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
await processMessage(message);
|
|
223
|
+
|
|
224
|
+
// Acknowledge by returning 2xx
|
|
225
|
+
res.status(200).send('OK');
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error('Error processing message:', error);
|
|
228
|
+
// Return 5xx to retry
|
|
229
|
+
res.status(500).send('Error');
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## 4. Firestore Patterns
|
|
237
|
+
|
|
238
|
+
### Single Document Pattern
|
|
239
|
+
|
|
240
|
+
```javascript
|
|
241
|
+
// Get with caching
|
|
242
|
+
async function getUser(userId) {
|
|
243
|
+
const cacheKey = `user:${userId}`;
|
|
244
|
+
|
|
245
|
+
// Check cache
|
|
246
|
+
const cached = await cache.get(cacheKey);
|
|
247
|
+
if (cached) return cached;
|
|
248
|
+
|
|
249
|
+
// Fetch from Firestore
|
|
250
|
+
const doc = await firestore.collection('users').doc(userId).get();
|
|
251
|
+
|
|
252
|
+
if (!doc.exists) return null;
|
|
253
|
+
|
|
254
|
+
const user = { id: doc.id, ...doc.data() };
|
|
255
|
+
|
|
256
|
+
// Cache for 5 minutes
|
|
257
|
+
await cache.set(cacheKey, user, 300);
|
|
258
|
+
|
|
259
|
+
return user;
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Batch Operations
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
// Batch write for better performance
|
|
267
|
+
async function createUsers(users) {
|
|
268
|
+
const batch = firestore.batch();
|
|
269
|
+
|
|
270
|
+
users.forEach(user => {
|
|
271
|
+
const ref = firestore.collection('users').doc();
|
|
272
|
+
batch.set(ref, {
|
|
273
|
+
...user,
|
|
274
|
+
createdAt: Firestore.FieldValue.serverTimestamp()
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
await batch.commit();
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## 5. Cloud Build CI/CD
|
|
285
|
+
|
|
286
|
+
```yaml
|
|
287
|
+
# cloudbuild.yaml
|
|
288
|
+
steps:
|
|
289
|
+
# Run tests
|
|
290
|
+
- name: 'node:20'
|
|
291
|
+
entrypoint: 'npm'
|
|
292
|
+
args: ['ci']
|
|
293
|
+
|
|
294
|
+
- name: 'node:20'
|
|
295
|
+
entrypoint: 'npm'
|
|
296
|
+
args: ['test']
|
|
297
|
+
|
|
298
|
+
# Build container
|
|
299
|
+
- name: 'gcr.io/cloud-builders/docker'
|
|
300
|
+
args: ['build', '-t', 'gcr.io/$PROJECT_ID/my-service:$COMMIT_SHA', '.']
|
|
301
|
+
|
|
302
|
+
# Push to registry
|
|
303
|
+
- name: 'gcr.io/cloud-builders/docker'
|
|
304
|
+
args: ['push', 'gcr.io/$PROJECT_ID/my-service:$COMMIT_SHA']
|
|
305
|
+
|
|
306
|
+
# Deploy to Cloud Run
|
|
307
|
+
- name: 'gcr.io/cloud-builders/gcloud'
|
|
308
|
+
args:
|
|
309
|
+
- 'run'
|
|
310
|
+
- 'deploy'
|
|
311
|
+
- 'my-service'
|
|
312
|
+
- '--image=gcr.io/$PROJECT_ID/my-service:$COMMIT_SHA'
|
|
313
|
+
- '--region=us-central1'
|
|
314
|
+
- '--platform=managed'
|
|
315
|
+
|
|
316
|
+
images:
|
|
317
|
+
- 'gcr.io/$PROJECT_ID/my-service:$COMMIT_SHA'
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## 6. Anti-Patterns
|
|
323
|
+
|
|
324
|
+
### ❌ No Min Instances
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
# WRONG: Cold start on every request after idle
|
|
328
|
+
gcloud run deploy --min-instances 0
|
|
329
|
+
|
|
330
|
+
# CORRECT: Keep warm for latency-sensitive apps
|
|
331
|
+
gcloud run deploy --min-instances 1
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### ❌ Global State Mutation
|
|
335
|
+
|
|
336
|
+
```javascript
|
|
337
|
+
// WRONG: Global state across requests
|
|
338
|
+
let requestCount = 0;
|
|
339
|
+
|
|
340
|
+
app.get('/', (req, res) => {
|
|
341
|
+
requestCount++; // Unreliable across instances!
|
|
342
|
+
res.json({ count: requestCount });
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// CORRECT: Use external storage
|
|
346
|
+
app.get('/', async (req, res) => {
|
|
347
|
+
const count = await redis.incr('request_count');
|
|
348
|
+
res.json({ count });
|
|
349
|
+
});
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Related Skills
|
|
355
|
+
|
|
356
|
+
- `aws-serverless` - Compare with AWS
|
|
357
|
+
- `azure-functions` - Compare with Azure
|
|
358
|
+
- `docker-expert` - Container optimization
|