container-superposition 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/README.md +24 -15
  2. package/dist/scripts/init.js +1 -1534
  3. package/dist/scripts/init.js.map +1 -1
  4. package/dist/tool/cli/args.d.ts +20 -0
  5. package/dist/tool/cli/args.d.ts.map +1 -0
  6. package/dist/tool/cli/args.js +325 -0
  7. package/dist/tool/cli/args.js.map +1 -0
  8. package/dist/tool/cli/run.d.ts +2 -0
  9. package/dist/tool/cli/run.d.ts.map +1 -0
  10. package/dist/tool/cli/run.js +318 -0
  11. package/dist/tool/cli/run.js.map +1 -0
  12. package/dist/tool/commands/adopt.d.ts.map +1 -1
  13. package/dist/tool/commands/adopt.js +1 -27
  14. package/dist/tool/commands/adopt.js.map +1 -1
  15. package/dist/tool/commands/doctor.d.ts +3 -0
  16. package/dist/tool/commands/doctor.d.ts.map +1 -1
  17. package/dist/tool/commands/doctor.js +1068 -70
  18. package/dist/tool/commands/doctor.js.map +1 -1
  19. package/dist/tool/commands/explain.d.ts.map +1 -1
  20. package/dist/tool/commands/explain.js +18 -0
  21. package/dist/tool/commands/explain.js.map +1 -1
  22. package/dist/tool/commands/migrate.d.ts +7 -0
  23. package/dist/tool/commands/migrate.d.ts.map +1 -0
  24. package/dist/tool/commands/migrate.js +52 -0
  25. package/dist/tool/commands/migrate.js.map +1 -0
  26. package/dist/tool/questionnaire/answers.d.ts +16 -0
  27. package/dist/tool/questionnaire/answers.d.ts.map +1 -0
  28. package/dist/tool/questionnaire/answers.js +102 -0
  29. package/dist/tool/questionnaire/answers.js.map +1 -0
  30. package/dist/tool/questionnaire/composer.d.ts +3 -3
  31. package/dist/tool/questionnaire/composer.d.ts.map +1 -1
  32. package/dist/tool/questionnaire/composer.js +902 -37
  33. package/dist/tool/questionnaire/composer.js.map +1 -1
  34. package/dist/tool/questionnaire/presets.d.ts +60 -0
  35. package/dist/tool/questionnaire/presets.d.ts.map +1 -0
  36. package/dist/tool/questionnaire/presets.js +164 -0
  37. package/dist/tool/questionnaire/presets.js.map +1 -0
  38. package/dist/tool/questionnaire/questionnaire.d.ts +10 -0
  39. package/dist/tool/questionnaire/questionnaire.d.ts.map +1 -0
  40. package/dist/tool/questionnaire/questionnaire.js +580 -0
  41. package/dist/tool/questionnaire/questionnaire.js.map +1 -0
  42. package/dist/tool/schema/manifest-migrations.d.ts +5 -0
  43. package/dist/tool/schema/manifest-migrations.d.ts.map +1 -1
  44. package/dist/tool/schema/manifest-migrations.js +45 -0
  45. package/dist/tool/schema/manifest-migrations.js.map +1 -1
  46. package/dist/tool/schema/overlay-loader.d.ts.map +1 -1
  47. package/dist/tool/schema/overlay-loader.js +25 -0
  48. package/dist/tool/schema/overlay-loader.js.map +1 -1
  49. package/dist/tool/schema/project-config.d.ts +14 -2
  50. package/dist/tool/schema/project-config.d.ts.map +1 -1
  51. package/dist/tool/schema/project-config.js +277 -34
  52. package/dist/tool/schema/project-config.js.map +1 -1
  53. package/dist/tool/schema/target-rules.d.ts +78 -0
  54. package/dist/tool/schema/target-rules.d.ts.map +1 -0
  55. package/dist/tool/schema/target-rules.js +367 -0
  56. package/dist/tool/schema/target-rules.js.map +1 -0
  57. package/dist/tool/schema/types.d.ts +123 -12
  58. package/dist/tool/schema/types.d.ts.map +1 -1
  59. package/dist/tool/utils/merge.d.ts.map +1 -1
  60. package/dist/tool/utils/merge.js +9 -0
  61. package/dist/tool/utils/merge.js.map +1 -1
  62. package/dist/tool/utils/parameters.d.ts +76 -0
  63. package/dist/tool/utils/parameters.d.ts.map +1 -0
  64. package/dist/tool/utils/parameters.js +125 -0
  65. package/dist/tool/utils/parameters.js.map +1 -0
  66. package/dist/tool/utils/paths.d.ts +2 -0
  67. package/dist/tool/utils/paths.d.ts.map +1 -0
  68. package/dist/tool/utils/paths.js +31 -0
  69. package/dist/tool/utils/paths.js.map +1 -0
  70. package/docs/creating-overlays.md +151 -2
  71. package/docs/deployment-targets.md +88 -56
  72. package/docs/examples.md +20 -17
  73. package/docs/filesystem-contract.md +5 -0
  74. package/docs/minimal-and-editor.md +65 -5
  75. package/docs/overlay-imports.md +202 -101
  76. package/docs/overlays.md +162 -34
  77. package/docs/quick-reference.md +99 -0
  78. package/docs/specs/003-mkdocs2-overlay/spec.md +114 -0
  79. package/docs/specs/004-doctor-fix/spec.md +70 -0
  80. package/docs/specs/005-cuda-overlay/spec.md +101 -0
  81. package/docs/specs/006-rocm-overlay/spec.md +109 -0
  82. package/docs/specs/007-init-project-file/spec.md +66 -0
  83. package/docs/specs/007-target-aware-generation/spec.md +126 -0
  84. package/docs/specs/008-project-file-canonical/spec.md +83 -0
  85. package/docs/specs/009-project-env/spec.md +147 -0
  86. package/docs/specs/010-compose-env-materialization/spec.md +130 -0
  87. package/docs/specs/011-overlay-parameters/spec.md +235 -0
  88. package/overlays/.shared/README.md +105 -21
  89. package/overlays/.shared/compose/common-healthchecks.md +60 -0
  90. package/overlays/.shared/compose/nvidia-gpu-devcontainer.yml +22 -0
  91. package/overlays/.shared/vscode/recommended-extensions.json +15 -11
  92. package/overlays/alertmanager/setup.sh +4 -19
  93. package/overlays/alertmanager/verify.sh +8 -9
  94. package/overlays/all/README.md +43 -0
  95. package/overlays/all/devcontainer.patch.json +6 -0
  96. package/overlays/all/overlay.yml +14 -0
  97. package/overlays/amp/setup.sh +5 -0
  98. package/overlays/bun/setup.sh +10 -1
  99. package/overlays/bun/verify.sh +6 -1
  100. package/overlays/claude-code/setup.sh +5 -0
  101. package/overlays/cloudflared/setup.sh +9 -12
  102. package/overlays/codex/README.md +9 -6
  103. package/overlays/codex/devcontainer.patch.json +7 -1
  104. package/overlays/codex/setup.sh +5 -0
  105. package/overlays/codex/verify.sh +8 -0
  106. package/overlays/comfyui/.env.example +34 -0
  107. package/overlays/comfyui/README.md +342 -0
  108. package/overlays/comfyui/devcontainer.patch.json +15 -0
  109. package/overlays/comfyui/docker-compose.yml +39 -0
  110. package/overlays/comfyui/overlay.yml +20 -0
  111. package/overlays/comfyui/setup.sh +36 -0
  112. package/overlays/comfyui/verify.sh +103 -0
  113. package/overlays/commitlint/setup.sh +5 -0
  114. package/overlays/cuda/README.md +179 -0
  115. package/overlays/cuda/devcontainer.patch.json +7 -0
  116. package/overlays/cuda/overlay.yml +17 -0
  117. package/overlays/cuda/setup.sh +32 -0
  118. package/overlays/cuda/verify.sh +38 -0
  119. package/overlays/devcontainer-cli/README.md +50 -0
  120. package/overlays/devcontainer-cli/devcontainer.patch.json +13 -0
  121. package/overlays/devcontainer-cli/overlay.yml +16 -0
  122. package/overlays/devcontainer-cli/setup.sh +14 -0
  123. package/overlays/direnv/devcontainer.patch.json +6 -0
  124. package/overlays/direnv/setup.sh +7 -6
  125. package/overlays/dotnet/setup.sh +14 -7
  126. package/overlays/duckdb/devcontainer.patch.json +1 -2
  127. package/overlays/gcloud/devcontainer.patch.json +0 -6
  128. package/overlays/gcloud/setup.sh +51 -0
  129. package/overlays/gemini-cli/setup.sh +5 -0
  130. package/overlays/git-helpers/devcontainer.patch.json +2 -1
  131. package/overlays/go/setup.sh +15 -14
  132. package/overlays/jaeger/overlay.yml +2 -0
  133. package/overlays/just/setup.sh +5 -17
  134. package/overlays/k3d/README.md +201 -0
  135. package/overlays/k3d/devcontainer.patch.json +9 -0
  136. package/overlays/k3d/overlay.yml +19 -0
  137. package/overlays/k3d/setup.sh +34 -0
  138. package/overlays/k3d/verify.sh +38 -0
  139. package/overlays/keycloak/docker-compose.yml +6 -4
  140. package/overlays/keycloak/verify.sh +4 -3
  141. package/overlays/kind/devcontainer.patch.json +1 -2
  142. package/overlays/kind/setup.sh +8 -17
  143. package/overlays/minio/setup.sh +10 -18
  144. package/overlays/mkdocs/overlay.yml +2 -1
  145. package/overlays/mkdocs2/README.md +135 -0
  146. package/overlays/mkdocs2/devcontainer.patch.json +19 -0
  147. package/overlays/mkdocs2/overlay.yml +17 -0
  148. package/overlays/mkdocs2/setup.sh +67 -0
  149. package/overlays/mkdocs2/verify.sh +35 -0
  150. package/overlays/modern-cli-tools/devcontainer.patch.json +7 -1
  151. package/overlays/modern-cli-tools/setup.sh +21 -71
  152. package/overlays/mongodb/devcontainer.patch.json +0 -6
  153. package/overlays/mongodb/setup.sh +59 -0
  154. package/overlays/mysql/verify.sh +4 -3
  155. package/overlays/nats/.env.example +1 -1
  156. package/overlays/nats/README.md +1 -1
  157. package/overlays/nats/docker-compose.yml +1 -1
  158. package/overlays/ngrok/setup.sh +9 -6
  159. package/overlays/nodejs/setup.sh +5 -0
  160. package/overlays/ollama/.env.example +14 -0
  161. package/overlays/ollama/README.md +325 -0
  162. package/overlays/ollama/devcontainer.patch.json +14 -0
  163. package/overlays/ollama/docker-compose.yml +24 -0
  164. package/overlays/ollama/overlay.yml +22 -0
  165. package/overlays/ollama/setup.sh +106 -0
  166. package/overlays/ollama/verify.sh +99 -0
  167. package/overlays/open-webui/.env.example +5 -0
  168. package/overlays/open-webui/README.md +162 -0
  169. package/overlays/open-webui/devcontainer.patch.json +14 -0
  170. package/overlays/open-webui/docker-compose.yml +23 -0
  171. package/overlays/open-webui/overlay.yml +38 -0
  172. package/overlays/openapi-tools/devcontainer.patch.json +1 -2
  173. package/overlays/openapi-tools/setup.sh +9 -8
  174. package/overlays/opencode/setup.sh +5 -0
  175. package/overlays/otel-collector/overlay.yml +2 -0
  176. package/overlays/otel-collector/setup.sh +3 -16
  177. package/overlays/otel-demo-nodejs/verify.sh +8 -9
  178. package/overlays/otel-demo-python/verify.sh +16 -10
  179. package/overlays/pandoc/README.md +22 -15
  180. package/overlays/pandoc/devcontainer.patch.json +6 -2
  181. package/overlays/pandoc/setup.sh +217 -18
  182. package/overlays/pandoc/verify.sh +16 -4
  183. package/overlays/pgvector/.env.example +6 -0
  184. package/overlays/pgvector/README.md +215 -0
  185. package/overlays/pgvector/devcontainer.patch.json +23 -0
  186. package/overlays/pgvector/docker-compose.yml +32 -0
  187. package/overlays/pgvector/overlay.yml +44 -0
  188. package/overlays/playwright/devcontainer.patch.json +3 -1
  189. package/overlays/playwright/setup.sh +37 -0
  190. package/overlays/postgres/.env.example +5 -5
  191. package/overlays/postgres/devcontainer.patch.json +4 -4
  192. package/overlays/postgres/docker-compose.yml +15 -5
  193. package/overlays/postgres/overlay.yml +19 -1
  194. package/overlays/powershell/setup.sh +49 -13
  195. package/overlays/pre-commit/setup.sh +12 -3
  196. package/overlays/prometheus/overlay.yml +2 -0
  197. package/overlays/promtail/verify.sh +16 -10
  198. package/overlays/pulumi/devcontainer.patch.json +1 -1
  199. package/overlays/python/setup.sh +28 -9
  200. package/overlays/python/verify.sh +4 -2
  201. package/overlays/qdrant/.env.example +4 -0
  202. package/overlays/qdrant/README.md +216 -0
  203. package/overlays/qdrant/devcontainer.patch.json +20 -0
  204. package/overlays/qdrant/docker-compose.yml +25 -0
  205. package/overlays/qdrant/overlay.yml +40 -0
  206. package/overlays/redpanda/docker-compose.yml +3 -5
  207. package/overlays/rocm/README.md +227 -0
  208. package/overlays/rocm/devcontainer.patch.json +4 -0
  209. package/overlays/rocm/overlay.yml +17 -0
  210. package/overlays/rocm/setup.sh +45 -0
  211. package/overlays/rocm/verify.sh +47 -0
  212. package/overlays/rust/setup.sh +11 -18
  213. package/overlays/skaffold/README.md +256 -0
  214. package/overlays/skaffold/devcontainer.patch.json +9 -0
  215. package/overlays/skaffold/overlay.yml +20 -0
  216. package/overlays/skaffold/setup.sh +33 -0
  217. package/overlays/skaffold/verify.sh +24 -0
  218. package/overlays/spec-kit/setup.sh +7 -3
  219. package/overlays/sqlite/setup.sh +14 -14
  220. package/overlays/sqlserver/docker-compose.yml +3 -3
  221. package/overlays/sqlserver/verify.sh +22 -5
  222. package/overlays/tempo/verify.sh +16 -10
  223. package/overlays/tilt/devcontainer.patch.json +1 -2
  224. package/overlays/tilt/setup.sh +14 -4
  225. package/overlays/windsurf-cli/setup.sh +27 -4
  226. package/overlays/windsurf-cli/verify.sh +13 -3
  227. package/package.json +4 -2
  228. package/templates/scripts/setup-utils.sh +228 -0
  229. package/tool/schema/config.schema.json +141 -9
  230. package/tool/schema/overlay-manifest.schema.json +38 -0
  231. package/overlays/.shared/compose/common-healthchecks.yml +0 -38
  232. /package/overlays/otel-demo-nodejs/{Dockerfile-otel-demo-nodejs → Dockerfile} +0 -0
  233. /package/overlays/otel-demo-nodejs/{package-otel-demo-nodejs.json → package.json} +0 -0
  234. /package/overlays/otel-demo-nodejs/{server-otel-demo-nodejs.js → server.js} +0 -0
  235. /package/overlays/otel-demo-nodejs/{tracing-otel-demo-nodejs.js → tracing.js} +0 -0
  236. /package/overlays/otel-demo-python/{Dockerfile-otel-demo-python → Dockerfile} +0 -0
  237. /package/overlays/otel-demo-python/{app-otel-demo-python.py → app.py} +0 -0
  238. /package/overlays/otel-demo-python/{requirements-otel-demo-python.txt → requirements.txt} +0 -0
@@ -4,18 +4,23 @@
4
4
  set -e
5
5
 
6
6
  echo "đŸ“Ļ Refreshing font cache..."
7
- sudo fc-cache -fv
7
+ sudo fc-cache -f
8
8
 
9
9
  echo "đŸ“Ļ Installing Pandoc (latest release)..."
10
10
  PANDOC_VERSION="3.6.4"
11
11
 
12
+ # Source shared setup utilities
13
+ # shellcheck source=setup-utils.sh
14
+ source "$(dirname "${BASH_SOURCE[0]}")/setup-utils.sh"
15
+ load_nvm
16
+
12
17
  if command -v apt-get > /dev/null 2>&1; then
13
18
  # Debian/Ubuntu — install official .deb from GitHub releases
14
19
  ARCH=$(dpkg --print-architecture)
15
20
  PANDOC_DEB="pandoc-${PANDOC_VERSION}-1-${ARCH}.deb"
16
21
  curl -fsSL "https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/${PANDOC_DEB}" \
17
22
  -o "/tmp/${PANDOC_DEB}"
18
- sudo dpkg -i "/tmp/${PANDOC_DEB}"
23
+ with_apt_lock sudo dpkg -i "/tmp/${PANDOC_DEB}"
19
24
  rm "/tmp/${PANDOC_DEB}"
20
25
  elif command -v apk > /dev/null 2>&1; then
21
26
  # Alpine Linux — install via apk (version may differ from pinned release)
@@ -26,7 +31,50 @@ else
26
31
  exit 1
27
32
  fi
28
33
 
29
- echo "✓ pandoc $(pandoc --version | head -1)"
34
+ if [ -x /usr/bin/pandoc ]; then
35
+ PANDOC_REAL_BIN="/usr/bin/pandoc"
36
+ elif [ -x /usr/local/bin/pandoc ] && [ ! -L /usr/local/bin/pandoc ]; then
37
+ PANDOC_REAL_BIN="/usr/local/bin/pandoc"
38
+ else
39
+ PANDOC_REAL_BIN="$(type -P pandoc || true)"
40
+ fi
41
+
42
+ if [ -z "$PANDOC_REAL_BIN" ] || [ ! -x "$PANDOC_REAL_BIN" ]; then
43
+ echo "❌ Could not resolve the real pandoc binary."
44
+ exit 1
45
+ fi
46
+
47
+ if [ "$PANDOC_REAL_BIN" = "/usr/local/bin/pandoc" ] && [ -f /usr/local/bin/pandoc ]; then
48
+ if grep -q 'DEFAULTS_FILE="\${HOME}/.pandoc/pandoc.yaml"' /usr/local/bin/pandoc 2>/dev/null; then
49
+ echo "❌ Refusing to wrap /usr/local/bin/pandoc because it is already the pandoc wrapper."
50
+ exit 1
51
+ fi
52
+ fi
53
+
54
+ echo "✓ pandoc $("$PANDOC_REAL_BIN" --version | head -1)"
55
+
56
+ echo "đŸ“Ļ Installing pandoc wrapper with default PDF settings..."
57
+ sudo tee /usr/local/bin/pandoc > /dev/null <<EOF
58
+ #!/bin/sh
59
+ REAL_PANDOC="${PANDOC_REAL_BIN}"
60
+ DEFAULTS_FILE="\${HOME}/.pandoc/pandoc.yaml"
61
+
62
+ for arg in "\$@"; do
63
+ case "\$arg" in
64
+ -d|--defaults|--defaults=*)
65
+ exec "\${REAL_PANDOC}" "\$@"
66
+ ;;
67
+ esac
68
+ done
69
+
70
+ if [ -f "\${DEFAULTS_FILE}" ]; then
71
+ exec "\${REAL_PANDOC}" --defaults "\${DEFAULTS_FILE}" "\$@"
72
+ fi
73
+
74
+ exec "\${REAL_PANDOC}" "\$@"
75
+ EOF
76
+ sudo chmod +x /usr/local/bin/pandoc
77
+ echo "✓ pandoc wrapper installed: /usr/local/bin/pandoc -> ${PANDOC_REAL_BIN}"
30
78
 
31
79
  echo "đŸ“Ļ Installing diagram.lua Lua filter..."
32
80
  mkdir -p "$HOME/.pandoc/filters"
@@ -34,17 +82,167 @@ curl -fsSL \
34
82
  "https://raw.githubusercontent.com/pandoc-ext/diagram/main/_extensions/diagram/diagram.lua" \
35
83
  -o "$HOME/.pandoc/filters/diagram.lua"
36
84
 
85
+ echo "đŸ“Ļ Writing emoji fallback Lua filter..."
86
+ cat > "$HOME/.pandoc/filters/emoji-fallback.lua" <<'EOF'
87
+ -- Replace emoji and flag glyphs that XeLaTeX commonly cannot render.
88
+ --
89
+ -- * BMP symbols (Miscellaneous Symbols U+2600-U+26FF, Dingbats U+2700-U+27BF)
90
+ -- are routed to the fallback font via \textfallback{} so they render as
91
+ -- real glyphs using Noto Sans Symbols 2 (declared by pandoc's fallbackfont
92
+ -- variable in pandoc.yaml).
93
+ --
94
+ -- * High-plane emoji (U+1F000-U+1FAFF: 🎉 🚀 etc.) are replaced with the
95
+ -- plain-text marker [emoji] because no common PDF font covers them fully.
96
+ --
97
+ -- * Flag sequences (pairs of regional-indicator letters: đŸ‡ē🇸 etc.) are
98
+ -- replaced with the two-letter country code in brackets, e.g. [US].
99
+ --
100
+ -- The filter is a no-op for all non-LaTeX output formats.
101
+
102
+ local REGIONAL_INDICATOR_A = 0x1F1E6
103
+ local REGIONAL_INDICATOR_Z = 0x1F1FF
104
+
105
+ local function is_regional_indicator(cp)
106
+ return cp >= REGIONAL_INDICATOR_A and cp <= REGIONAL_INDICATOR_Z
107
+ end
108
+
109
+ -- High-plane emoji that no standard PDF font renders reliably.
110
+ local function is_high_plane_emoji(cp)
111
+ return cp >= 0x1F000 and cp <= 0x1FAFF
112
+ end
113
+
114
+ -- BMP symbol blocks absent from Carlito (and most body fonts).
115
+ -- Characters in these ranges are routed to the \textfallback font instead.
116
+ local function is_bmp_symbol(cp)
117
+ return (cp >= 0x2600 and cp <= 0x26FF) -- Miscellaneous Symbols (⚠ ☀ ⛔ â€Ļ)
118
+ or (cp >= 0x2700 and cp <= 0x27BF) -- Dingbats (✅ ❌ ✓ â€Ļ)
119
+ end
120
+
121
+ -- Parse text into a list of pandoc inline elements, applying substitutions.
122
+ -- Returns nil when no substitutions are needed.
123
+ local function text_to_inlines(text)
124
+ if FORMAT ~= 'latex' then return nil end
125
+
126
+ -- Quick scan: skip AST work when there is nothing to process.
127
+ local needs_processing = false
128
+ for _, cp in utf8.codes(text) do
129
+ if is_regional_indicator(cp) or is_high_plane_emoji(cp) or is_bmp_symbol(cp)
130
+ or cp == 0x200D or cp == 0x20E3 or cp == 0xFE0F then
131
+ needs_processing = true
132
+ break
133
+ end
134
+ end
135
+ if not needs_processing then return nil end
136
+
137
+ local codepoints = {}
138
+ for _, cp in utf8.codes(text) do table.insert(codepoints, cp) end
139
+
140
+ local inlines = {}
141
+ local buf = {}
142
+
143
+ local function flush_buf()
144
+ if #buf > 0 then
145
+ table.insert(inlines, pandoc.Str(table.concat(buf)))
146
+ buf = {}
147
+ end
148
+ end
149
+
150
+ local i = 1
151
+ while i <= #codepoints do
152
+ local cp = codepoints[i]
153
+
154
+ -- Variation selectors / zero-width joiners / combining enclosing keycap
155
+ if cp == 0x200D or cp == 0x20E3 or cp == 0xFE0F then
156
+ i = i + 1
157
+
158
+ -- Flag sequences: two consecutive regional-indicator letters → [XX]
159
+ elseif is_regional_indicator(cp)
160
+ and i < #codepoints
161
+ and is_regional_indicator(codepoints[i + 1]) then
162
+ flush_buf()
163
+ local a = string.char((cp - REGIONAL_INDICATOR_A) + string.byte('A'))
164
+ local b = string.char((codepoints[i + 1] - REGIONAL_INDICATOR_A) + string.byte('A'))
165
+ table.insert(inlines, pandoc.Str('[' .. a .. b .. ']'))
166
+ i = i + 2
167
+
168
+ -- High-plane emoji → [emoji] text marker
169
+ elseif is_high_plane_emoji(cp) then
170
+ flush_buf()
171
+ table.insert(inlines, pandoc.Str('[emoji]'))
172
+ i = i + 1
173
+ -- Collapse consecutive emoji into one marker
174
+ while i <= #codepoints
175
+ and (is_high_plane_emoji(codepoints[i])
176
+ or codepoints[i] == 0xFE0F
177
+ or codepoints[i] == 0x200D) do
178
+ i = i + 1
179
+ end
180
+
181
+ -- BMP symbols → \textfallback{char} (renders with Noto Sans Symbols 2)
182
+ elseif is_bmp_symbol(cp) then
183
+ flush_buf()
184
+ table.insert(inlines, pandoc.RawInline('latex',
185
+ '\\textfallback{' .. utf8.char(cp) .. '}'))
186
+ i = i + 1
187
+ -- Skip trailing variation selector-16 (e.g. U+FE0F after ⚠)
188
+ if i <= #codepoints and codepoints[i] == 0xFE0F then i = i + 1 end
189
+
190
+ else
191
+ table.insert(buf, utf8.char(cp))
192
+ i = i + 1
193
+ end
194
+ end
195
+ flush_buf()
196
+
197
+ -- If result is a single identical Str, nothing actually changed.
198
+ if #inlines == 1 and inlines[1].t == 'Str' and inlines[1].text == text then
199
+ return nil
200
+ end
201
+
202
+ return inlines
203
+ end
204
+
205
+ function Str(el)
206
+ local inlines = text_to_inlines(el.text)
207
+ if inlines == nil then return nil end
208
+ return inlines
209
+ end
210
+
211
+ -- In code spans / code blocks we cannot use \textfallback (verbatim context),
212
+ -- so we simply drop the offending characters to prevent XeLaTeX warnings.
213
+ local function strip_unsupported(el)
214
+ if FORMAT ~= 'latex' then return nil end
215
+ local changed = false
216
+ local out = {}
217
+ for _, cp in utf8.codes(el.text) do
218
+ if is_high_plane_emoji(cp) or is_bmp_symbol(cp)
219
+ or cp == 0x200D or cp == 0x20E3 or cp == 0xFE0F then
220
+ changed = true
221
+ else
222
+ table.insert(out, utf8.char(cp))
223
+ end
224
+ end
225
+ if not changed then return nil end
226
+ el.text = table.concat(out)
227
+ return el
228
+ end
229
+
230
+ function Code(el) return strip_unsupported(el) end
231
+ function CodeBlock(el) return strip_unsupported(el) end
232
+ EOF
233
+
37
234
  echo "đŸ“Ļ Installing Mermaid CLI (requires Node.js)..."
38
235
  if command -v npm &>/dev/null; then
39
- npm install -g @mermaid-js/mermaid-cli
40
- # Point mmdc at the system Chromium (avoids Puppeteer downloading its own)
41
- mkdir -p "$HOME/.config/mermaid"
42
- cat > "$HOME/.config/mermaid/puppeteer-config.json" <<'EOF'
43
- {
44
- "executablePath": "/usr/bin/chromium",
45
- "args": ["--no-sandbox", "--disable-setuid-sandbox"]
46
- }
47
- EOF
236
+ run_spinner "Mermaid CLI" npm install -g @mermaid-js/mermaid-cli
237
+ # Create a chromium wrapper that always passes --no-sandbox (required in containers).
238
+ # This is more robust than configuring mmdc/puppeteer individually — any tool
239
+ # that launches chromium via PUPPETEER_EXECUTABLE_PATH gets the sandbox flags.
240
+ sudo tee /usr/local/bin/chromium-no-sandbox > /dev/null <<'WRAPPER'
241
+ #!/bin/sh
242
+ exec /usr/bin/chromium --no-sandbox --disable-setuid-sandbox "$@"
243
+ WRAPPER
244
+ sudo chmod +x /usr/local/bin/chromium-no-sandbox
245
+ echo "✓ chromium-no-sandbox wrapper installed"
48
246
  if command -v mmdc >/dev/null 2>&1; then
49
247
  echo "✓ Mermaid CLI installed: $(command -v mmdc)"
50
248
  else
@@ -56,8 +254,12 @@ fi
56
254
 
57
255
  echo "đŸ“Ļ Writing default pandoc.yaml..."
58
256
  mkdir -p "$HOME/.pandoc"
257
+ PANDOC_FILTERS_DIR="$HOME/.pandoc/filters"
59
258
  cat > "$HOME/.pandoc/pandoc.yaml" <<'EOF'
60
259
  pdf-engine: xelatex
260
+ filters:
261
+ - __PANDOC_FILTERS_DIR__/emoji-fallback.lua
262
+ - __PANDOC_FILTERS_DIR__/diagram.lua
61
263
 
62
264
  variables:
63
265
  mainfont: "Carlito"
@@ -82,13 +284,10 @@ variables:
82
284
  # toc: true
83
285
  # toc-depth: 3
84
286
  # number-sections: true
85
-
86
- # Uncomment to enable Mermaid/diagram rendering (requires nodejs overlay):
87
- # lua-filter:
88
- # - ~/.pandoc/filters/diagram.lua
89
287
  EOF
288
+ sed -i "s|__PANDOC_FILTERS_DIR__|${PANDOC_FILTERS_DIR}|g" "$HOME/.pandoc/pandoc.yaml"
90
289
 
91
290
  echo ""
92
291
  echo "✓ pandoc overlay setup complete"
93
- echo "â„šī¸ Build a PDF: pandoc -d ~/.pandoc/pandoc.yaml doc.md -o doc.pdf"
94
- echo "â„šī¸ With Mermaid: pandoc -d ~/.pandoc/pandoc.yaml --lua-filter ~/.pandoc/filters/diagram.lua doc.md -o doc.pdf"
292
+ echo "â„šī¸ Build a PDF: pandoc doc.md -o doc.pdf"
293
+ echo "â„šī¸ With Mermaid diagrams: pandoc doc.md -o doc.pdf (diagram.lua enabled by default)"
@@ -3,11 +3,23 @@ set -e
3
3
  command -v pandoc || { echo "✗ pandoc not found"; exit 1; }
4
4
  command -v xelatex || { echo "✗ xelatex not found"; exit 1; }
5
5
  [ -f "$HOME/.pandoc/filters/diagram.lua" ] || { echo "✗ diagram.lua not found"; exit 1; }
6
- [ -f "$HOME/.pandoc/pandoc.yaml" ] || { echo "✗ pandoc.yaml not found"; exit 1; }
6
+ [ -f "$HOME/.pandoc/filters/emoji-fallback.lua" ] || { echo "✗ emoji-fallback.lua not found"; exit 1; }
7
+ [ -f "$HOME/.pandoc/pandoc.yaml" ] || { echo "✗ pandoc.yaml not found"; exit 1; }
7
8
 
8
- # Smoke test: render a trivial PDF
9
- echo "# Test" | pandoc --pdf-engine=xelatex -o /tmp/pandoc-verify-test.pdf && \
10
- echo "✓ pandoc PDF smoke test passed" || { echo "✗ pandoc PDF render failed"; exit 1; }
9
+ # Smoke test: render a PDF that includes generic emoji and flag sequences
10
+ # which XeLaTeX can reject without the fallback filter.
11
+ printf '%s\n' \
12
+ '# Unicode Smoke Test' \
13
+ '' \
14
+ 'Status icons: ✅ âš ī¸ ❌' \
15
+ 'Activity emoji: 🎉 🚀 đŸ§Ē' \
16
+ 'Flag emoji: đŸ‡ē🇸 đŸ‡¯đŸ‡ĩ 🇧🇷' \
17
+ 'Mixed sample: Unicode check 😀' |
18
+ pandoc -o /tmp/pandoc-verify-test.pdf &&
19
+ echo "✓ pandoc Unicode PDF smoke test passed" || {
20
+ echo "✗ pandoc Unicode PDF render failed"
21
+ exit 1
22
+ }
11
23
 
12
24
  rm -f /tmp/pandoc-verify-test.pdf
13
25
  echo "✓ pandoc overlay: OK"
@@ -0,0 +1,6 @@
1
+ # pgvector Configuration
2
+ PGVECTOR_VERSION={{cs.PGVECTOR_VERSION}}
3
+ PGVECTOR_DB={{cs.PGVECTOR_DB}}
4
+ PGVECTOR_USER={{cs.PGVECTOR_USER}}
5
+ PGVECTOR_PASSWORD={{cs.PGVECTOR_PASSWORD}}
6
+ PGVECTOR_PORT={{cs.PGVECTOR_PORT}}
@@ -0,0 +1,215 @@
1
+ # pgvector Overlay
2
+
3
+ Adds a PostgreSQL 16 database pre-loaded with the [pgvector](https://github.com/pgvector/pgvector) extension, enabling vector similarity search directly within a familiar SQL environment.
4
+
5
+ > **Note:** This overlay conflicts with the `postgres` overlay because both provide a PostgreSQL service on port 5432. Choose `pgvector` when you need vector search, or `postgres` for a plain database.
6
+
7
+ ## Features
8
+
9
+ - **pgvector extension** — `CREATE EXTENSION vector;` is available immediately; no manual installation required
10
+ - **Full PostgreSQL 16** — All standard SQL features alongside vector operations
11
+ - **`psql` client in devcontainer** — `postgresql-client` package installed for interactive SQL sessions
12
+ - **Pre-configured environment** — `PGHOST`, `PGPORT`, `PGDATABASE`, and `PGUSER` set automatically
13
+ - **Persistent storage** — Named Docker volume preserves data across container rebuilds
14
+ - **Health check** — Compose readiness check using `pg_isready` before dependent services start
15
+
16
+ ## How It Works
17
+
18
+ pgvector runs as a Docker Compose service (`pgvector`) alongside your devcontainer, using the official `pgvector/pgvector:pg16` image, which bundles PostgreSQL 16 with the pgvector extension pre-compiled and ready to use.
19
+
20
+ **Service configuration:**
21
+
22
+ - Image: `pgvector/pgvector:pg16`
23
+ - Network: `devnet` (shared with the dev container)
24
+ - Port: `5432` (customisable via `PGVECTOR_PORT`)
25
+ - Volume: `pgvector-data` for persistent data
26
+
27
+ The following environment variables are pre-set in the devcontainer for seamless `psql` usage:
28
+
29
+ | Variable | Value |
30
+ | ------------ | ---------- |
31
+ | `PGHOST` | `pgvector` |
32
+ | `PGPORT` | `5432` |
33
+ | `PGDATABASE` | `devdb` |
34
+ | `PGUSER` | `postgres` |
35
+
36
+ ## Getting Started
37
+
38
+ ```bash
39
+ # Connect to the database
40
+ psql -h pgvector -U postgres -d devdb
41
+
42
+ # Enable the vector extension (run once per database)
43
+ psql -h pgvector -U postgres -d devdb -c "CREATE EXTENSION IF NOT EXISTS vector;"
44
+
45
+ # Verify the extension is installed
46
+ psql -h pgvector -U postgres -d devdb -c "SELECT * FROM pg_extension WHERE extname = 'vector';"
47
+ ```
48
+
49
+ ## Common Commands
50
+
51
+ ### Vector Operations in SQL
52
+
53
+ ```sql
54
+ -- Enable the extension in your database
55
+ CREATE EXTENSION IF NOT EXISTS vector;
56
+
57
+ -- Create a table with a vector column (1536 dimensions for OpenAI embeddings)
58
+ CREATE TABLE documents (
59
+ id SERIAL PRIMARY KEY,
60
+ content TEXT,
61
+ embedding vector(1536)
62
+ );
63
+
64
+ -- Insert a record with an embedding
65
+ INSERT INTO documents (content, embedding)
66
+ VALUES ('Hello, world!', '[0.1, 0.2, 0.3, ...]');
67
+
68
+ -- Cosine similarity search (find closest neighbours)
69
+ SELECT id, content, 1 - (embedding <=> '[0.1, 0.2, 0.3, ...]') AS similarity
70
+ FROM documents
71
+ ORDER BY embedding <=> '[0.1, 0.2, 0.3, ...]'
72
+ LIMIT 5;
73
+
74
+ -- Create an HNSW index for faster approximate search
75
+ CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
76
+
77
+ -- Create an IVFFlat index for large datasets
78
+ CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
79
+ ```
80
+
81
+ ### Database Management
82
+
83
+ ```bash
84
+ # Connect interactively
85
+ psql
86
+
87
+ # Run a SQL file
88
+ psql -f schema.sql
89
+
90
+ # Dump the database
91
+ pg_dump -h pgvector -U postgres devdb > backup.sql
92
+
93
+ # Restore from dump
94
+ psql -h pgvector -U postgres devdb < backup.sql
95
+ ```
96
+
97
+ ### Python Integration
98
+
99
+ ```bash
100
+ pip install psycopg2-binary pgvector
101
+
102
+ python - <<'EOF'
103
+ import psycopg2
104
+ from pgvector.psycopg2 import register_vector
105
+ import numpy as np
106
+
107
+ conn = psycopg2.connect(
108
+ host="pgvector", port=5432,
109
+ dbname="devdb", user="postgres", password="postgres"
110
+ )
111
+ register_vector(conn)
112
+ cur = conn.cursor()
113
+
114
+ cur.execute("CREATE EXTENSION IF NOT EXISTS vector")
115
+ cur.execute("CREATE TABLE IF NOT EXISTS items (id serial PRIMARY KEY, embedding vector(3))")
116
+ cur.execute("INSERT INTO items (embedding) VALUES (%s)", (np.array([1, 2, 3]),))
117
+ conn.commit()
118
+
119
+ cur.execute("SELECT * FROM items ORDER BY embedding <-> %s LIMIT 5", (np.array([1, 2, 4]),))
120
+ print(cur.fetchall())
121
+ conn.close()
122
+ EOF
123
+ ```
124
+
125
+ ## Configuration
126
+
127
+ ### Environment Variables
128
+
129
+ | Variable | Default | Description |
130
+ | ------------------- | ---------- | ------------------------------------------ |
131
+ | `PGVECTOR_VERSION` | `pg16` | pgvector image tag (e.g. `pg16`, `pg17`) |
132
+ | `PGVECTOR_DB` | `devdb` | Name of the database to create |
133
+ | `PGVECTOR_USER` | `postgres` | PostgreSQL superuser name |
134
+ | `PGVECTOR_PASSWORD` | `postgres` | PostgreSQL superuser password |
135
+ | `PGVECTOR_PORT` | `5432` | Host port mapped to the PostgreSQL service |
136
+
137
+ Copy `.devcontainer/.env.example` to `.devcontainer/.env` and customize.
138
+
139
+ ## pgvector vs Qdrant
140
+
141
+ | Feature | pgvector (this overlay) | Qdrant |
142
+ | -------------------- | ------------------------------- | ------------------------ |
143
+ | **Storage model** | ✅ Relational + vector | Vector-only |
144
+ | **SQL support** | ✅ Full PostgreSQL SQL | ❌ REST/gRPC API only |
145
+ | **Familiar tooling** | ✅ psql, ORMs, existing schema | New client SDKs required |
146
+ | **Query language** | ✅ SQL with `<->`, `<=>`, `<#>` | âš ī¸ JSON query DSL |
147
+ | **Filtering** | ✅ SQL WHERE clause | ✅ Payload-based filters |
148
+ | **Scale** | âš ī¸ Vertical scaling preferred | ✅ Distributed by design |
149
+ | **Index types** | IVFFlat, HNSW | HNSW |
150
+
151
+ **Choose pgvector when:**
152
+
153
+ - You already use PostgreSQL and want to add vectors to existing tables
154
+ - You need to join vector results with relational data
155
+ - Your team is comfortable with SQL
156
+
157
+ **Choose Qdrant when:**
158
+
159
+ - Vectors are your primary data model
160
+ - You need purpose-built vector search performance at scale
161
+ - You want advanced filtering and payload storage
162
+
163
+ ## Use Cases
164
+
165
+ - **RAG pipelines** — Store document embeddings alongside metadata; retrieve context with SQL joins
166
+ - **Semantic search** — Add vector search to an existing PostgreSQL application without a second database
167
+ - **Hybrid search** — Combine full-text search (`tsvector`) with vector similarity in a single query
168
+ - **Recommendation systems** — Store and query user or item embeddings with familiar SQL tooling
169
+
170
+ **Integrates well with:**
171
+
172
+ - `ollama` — Generate embeddings locally with `nomic-embed-text` or similar models
173
+ - `python` — `psycopg2` + `pgvector` Python package for seamless integration
174
+ - `nodejs` — `pg` driver with raw SQL for vector operations
175
+
176
+ ## Troubleshooting
177
+
178
+ ### Extension Not Found
179
+
180
+ If `CREATE EXTENSION vector` fails:
181
+
182
+ ```bash
183
+ # Confirm pgvector image (not plain postgres) is in use
184
+ docker compose ps pgvector
185
+
186
+ # Check available extensions
187
+ psql -h pgvector -U postgres -c "SELECT name FROM pg_available_extensions WHERE name = 'vector';"
188
+ ```
189
+
190
+ ### Port Conflict with postgres Overlay
191
+
192
+ The `pgvector` and `postgres` overlays both bind port 5432 and conflict with each other. Select only one.
193
+
194
+ ### Connection Refused
195
+
196
+ ```bash
197
+ # Check service health
198
+ docker compose ps pgvector
199
+ docker compose logs pgvector
200
+ ```
201
+
202
+ ## References
203
+
204
+ - [pgvector GitHub](https://github.com/pgvector/pgvector)
205
+ - [pgvector Docker Image](https://hub.docker.com/r/pgvector/pgvector)
206
+ - [PostgreSQL Documentation](https://www.postgresql.org/docs/16/)
207
+ - [pgvector Python package](https://github.com/pgvector/pgvector-python)
208
+ - [pgvector Node.js package](https://github.com/pgvector/pgvector-node)
209
+
210
+ **Related Overlays:**
211
+
212
+ - [`postgres`](../postgres/README.md) — Plain PostgreSQL without pgvector (conflicts)
213
+ - [`qdrant`](../qdrant/README.md) — Purpose-built vector database (alternative)
214
+ - [`ollama`](../ollama/README.md) — Local embedding generation
215
+ - [`python`](../python/README.md) — Python development environment
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.base.schema.json",
3
+ "runServices": ["pgvector"],
4
+ "forwardPorts": [5432],
5
+ "portsAttributes": {
6
+ "5432": {
7
+ "label": "pgvector database",
8
+ "onAutoForward": "notify"
9
+ }
10
+ },
11
+ "features": {
12
+ "./features/cross-distro-packages": {
13
+ "apt": "postgresql-client",
14
+ "apk": "postgresql-client"
15
+ }
16
+ },
17
+ "containerEnv": {
18
+ "PGHOST": "pgvector",
19
+ "PGPORT": "5432",
20
+ "PGDATABASE": "devdb",
21
+ "PGUSER": "postgres"
22
+ }
23
+ }
@@ -0,0 +1,32 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ pgvector:
5
+ image: pgvector/pgvector:${PGVECTOR_VERSION:-{{cs.PGVECTOR_VERSION}}}
6
+ restart: unless-stopped
7
+ volumes:
8
+ - pgvector-data:/var/lib/postgresql/data
9
+ environment:
10
+ POSTGRES_DB: ${PGVECTOR_DB:-{{cs.PGVECTOR_DB}}}
11
+ POSTGRES_USER: ${PGVECTOR_USER:-{{cs.PGVECTOR_USER}}}
12
+ POSTGRES_PASSWORD: ${PGVECTOR_PASSWORD:-{{cs.PGVECTOR_PASSWORD}}}
13
+ ports:
14
+ - '${PGVECTOR_PORT:-{{cs.PGVECTOR_PORT}}}:5432'
15
+ networks:
16
+ - devnet
17
+ healthcheck:
18
+ test:
19
+ [
20
+ 'CMD-SHELL',
21
+ 'pg_isready -U ${PGVECTOR_USER:-{{cs.PGVECTOR_USER}}} -d ${PGVECTOR_DB:-{{cs.PGVECTOR_DB}}}',
22
+ ]
23
+ interval: 10s
24
+ timeout: 5s
25
+ retries: 5
26
+ start_period: 10s
27
+
28
+ volumes:
29
+ pgvector-data:
30
+
31
+ networks:
32
+ devnet:
@@ -0,0 +1,44 @@
1
+ id: pgvector
2
+ name: pgvector (PostgreSQL + vector)
3
+ description: PostgreSQL 16 with the pgvector extension for vector similarity search
4
+ category: database
5
+ supports:
6
+ - compose
7
+ requires: []
8
+ suggests:
9
+ - ollama
10
+ - python
11
+ - nodejs
12
+ conflicts:
13
+ - postgres
14
+ tags:
15
+ - database
16
+ - sql
17
+ - vector
18
+ - embeddings
19
+ - postgres
20
+ - pgvector
21
+ ports:
22
+ - port: 5432
23
+ service: pgvector
24
+ protocol: tcp
25
+ description: pgvector database connection
26
+ onAutoForward: notify
27
+ connectionStringTemplate: 'postgresql://{pgvector_user}:{pgvector_password}@{host}:{port}/{pgvector_db}'
28
+ parameters:
29
+ PGVECTOR_DB:
30
+ description: Name of the database to create
31
+ default: devdb
32
+ PGVECTOR_USER:
33
+ description: PostgreSQL superuser name
34
+ default: postgres
35
+ PGVECTOR_PASSWORD:
36
+ description: PostgreSQL superuser password
37
+ default: postgres
38
+ sensitive: true
39
+ PGVECTOR_PORT:
40
+ description: Host port mapped to pgvector (5432 inside container)
41
+ default: '5432'
42
+ PGVECTOR_VERSION:
43
+ description: pgvector/pgvector image tag (e.g. pg16, pg17)
44
+ default: pg16
@@ -5,5 +5,7 @@
5
5
  "version": "lts"
6
6
  }
7
7
  },
8
- "postCreateCommand": "npx -y playwright install --with-deps chromium"
8
+ "postCreateCommand": {
9
+ "setup-playwright": "bash .devcontainer/scripts/setup-playwright.sh"
10
+ }
9
11
  }
@@ -0,0 +1,37 @@
1
+ #!/bin/bash
2
+ # Playwright setup script — Install Chromium browser + system dependencies
3
+
4
+ set -e
5
+
6
+ # Source shared setup utilities (provides load_nvm, acquire_apt_lock)
7
+ # shellcheck source=setup-utils.sh
8
+ source "$(dirname "${BASH_SOURCE[0]}")/setup-utils.sh"
9
+ load_nvm
10
+
11
+ echo "🎭 Setting up Playwright..."
12
+
13
+ # Suppress Playwright's "WARNING: no project dependencies" banner, apt output,
14
+ # and download progress bars.
15
+ export DEBIAN_FRONTEND=noninteractive
16
+ export CI=1 # suppresses interactive warnings inside playwright install
17
+
18
+ # Install Chromium and its system dependencies.
19
+ # playwright install --with-deps calls apt-get internally; we must hold the
20
+ # shared apt lock so it does not race with other parallel setup scripts.
21
+ acquire_apt_lock
22
+ npx -y playwright install --with-deps chromium 2>&1 \
23
+ | grep -vEe '^[╔║╚]' \
24
+ -e '^\|[[:space:]■]*\|' \
25
+ -e '^(Get:|Fetched|Reading|Building dependency|Processing triggers|Setting up|Preparing to|Selecting previously|Unpacking|Need to get|After this operation|The following )' \
26
+ -e '(is already the newest version|set to manually installed|npm notice|debconf:|update-alternatives:)' \
27
+ -e '^\(Reading database'
28
+ rc=${PIPESTATUS[0]}
29
+ release_apt_lock
30
+
31
+ if [ "$rc" -ne 0 ]; then
32
+ echo "❌ Playwright browser installation failed (exit $rc)"
33
+ exit "$rc"
34
+ fi
35
+
36
+ echo "✓ Playwright setup complete"
37
+ echo "â„šī¸ Run 'npx playwright test' to execute tests"