@shetty4l/core 0.1.3
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/.github/workflows/ci-shared.yml +36 -0
- package/.github/workflows/ci.yml +14 -0
- package/.github/workflows/release-shared.yml +150 -0
- package/.github/workflows/release.yml +19 -0
- package/.husky/pre-commit +3 -0
- package/README.md +88 -0
- package/biome.json +12 -0
- package/bun.lock +65 -0
- package/package.json +37 -0
- package/scripts/install-lib.sh +149 -0
- package/src/cli.ts +109 -0
- package/src/config.ts +200 -0
- package/src/daemon.ts +204 -0
- package/src/http.ts +138 -0
- package/src/index.ts +21 -0
- package/src/result.ts +28 -0
- package/src/scripts/version-bump.ts +144 -0
- package/src/signals.ts +69 -0
- package/src/version.ts +27 -0
- package/test/cli.test.ts +61 -0
- package/test/config.test.ts +263 -0
- package/test/daemon.test.ts +89 -0
- package/test/http.test.ts +152 -0
- package/test/result.test.ts +58 -0
- package/test/signals.test.ts +25 -0
- package/test/version.test.ts +55 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: CI (shared)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
extra-validate-command:
|
|
7
|
+
description: "Optional extra command to run after validate (e.g. a full test suite)"
|
|
8
|
+
required: false
|
|
9
|
+
type: string
|
|
10
|
+
default: ""
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
validate:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- name: Checkout
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Setup Bun
|
|
23
|
+
uses: oven-sh/setup-bun@v2
|
|
24
|
+
with:
|
|
25
|
+
bun-version: latest
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: bun install --frozen-lockfile
|
|
29
|
+
|
|
30
|
+
- name: Validate
|
|
31
|
+
run: bun run validate
|
|
32
|
+
|
|
33
|
+
- name: Extra validation
|
|
34
|
+
if: ${{ inputs.extra-validate-command != '' }}
|
|
35
|
+
shell: bash
|
|
36
|
+
run: ${{ inputs.extra-validate-command }}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
name: Release (shared)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
service-name:
|
|
7
|
+
description: "Service name for tarball prefix and release title (e.g. engram, synapse)"
|
|
8
|
+
required: true
|
|
9
|
+
type: string
|
|
10
|
+
extra-tarball-paths:
|
|
11
|
+
description: "Space-separated additional paths to include in tarball (e.g. 'opencode/ scripts/install.sh')"
|
|
12
|
+
required: false
|
|
13
|
+
type: string
|
|
14
|
+
default: ""
|
|
15
|
+
publish-npm:
|
|
16
|
+
description: "Whether to publish to GitHub Packages via npm publish"
|
|
17
|
+
required: false
|
|
18
|
+
type: boolean
|
|
19
|
+
default: false
|
|
20
|
+
attach-install-sh:
|
|
21
|
+
description: "Whether to attach scripts/install.sh as a release asset"
|
|
22
|
+
required: false
|
|
23
|
+
type: boolean
|
|
24
|
+
default: false
|
|
25
|
+
|
|
26
|
+
concurrency:
|
|
27
|
+
group: release
|
|
28
|
+
cancel-in-progress: false
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
release:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
permissions:
|
|
34
|
+
contents: write
|
|
35
|
+
packages: write
|
|
36
|
+
steps:
|
|
37
|
+
- name: Checkout
|
|
38
|
+
uses: actions/checkout@v4
|
|
39
|
+
with:
|
|
40
|
+
ref: ${{ github.event.workflow_run.head_sha }}
|
|
41
|
+
fetch-depth: 0
|
|
42
|
+
|
|
43
|
+
- name: Setup Bun
|
|
44
|
+
uses: oven-sh/setup-bun@v2
|
|
45
|
+
with:
|
|
46
|
+
bun-version: latest
|
|
47
|
+
|
|
48
|
+
- name: Install dependencies
|
|
49
|
+
run: bun install --frozen-lockfile
|
|
50
|
+
|
|
51
|
+
- name: Compute next version
|
|
52
|
+
id: version
|
|
53
|
+
run: |
|
|
54
|
+
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.1.0")
|
|
55
|
+
echo "latest_tag=${LATEST_TAG}" >> "$GITHUB_OUTPUT"
|
|
56
|
+
|
|
57
|
+
VERSION="${LATEST_TAG#v}"
|
|
58
|
+
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
|
|
59
|
+
|
|
60
|
+
COMMITS_SINCE="$(git log "${LATEST_TAG}..HEAD" --format='%s' 2>/dev/null || echo "")"
|
|
61
|
+
if echo "$COMMITS_SINCE" | grep -q '\[major\]'; then
|
|
62
|
+
NEXT_VERSION="$((MAJOR + 1)).0.0"
|
|
63
|
+
BUMP_LEVEL="major"
|
|
64
|
+
elif echo "$COMMITS_SINCE" | grep -q '\[minor\]'; then
|
|
65
|
+
NEXT_VERSION="${MAJOR}.$((MINOR + 1)).0"
|
|
66
|
+
BUMP_LEVEL="minor"
|
|
67
|
+
else
|
|
68
|
+
NEXT_VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))"
|
|
69
|
+
BUMP_LEVEL="patch"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
echo "bump_level=${BUMP_LEVEL}" >> "$GITHUB_OUTPUT"
|
|
73
|
+
echo "next_version=${NEXT_VERSION}" >> "$GITHUB_OUTPUT"
|
|
74
|
+
echo "next_tag=v${NEXT_VERSION}" >> "$GITHUB_OUTPUT"
|
|
75
|
+
echo "Releasing: v${NEXT_VERSION} (previous: ${LATEST_TAG}, bump: ${BUMP_LEVEL})"
|
|
76
|
+
|
|
77
|
+
- name: Write VERSION file
|
|
78
|
+
run: echo "${{ steps.version.outputs.next_version }}" > VERSION
|
|
79
|
+
|
|
80
|
+
- name: Update package.json version
|
|
81
|
+
run: |
|
|
82
|
+
jq --arg v "${{ steps.version.outputs.next_version }}" '.version = $v' package.json > tmp.json
|
|
83
|
+
mv tmp.json package.json
|
|
84
|
+
|
|
85
|
+
- name: Write BUILD_META.json
|
|
86
|
+
run: |
|
|
87
|
+
SHA="${{ github.event.workflow_run.head_sha }}"
|
|
88
|
+
SHORT_SHA="${SHA:0:7}"
|
|
89
|
+
BRANCH="${{ github.event.workflow_run.head_branch }}"
|
|
90
|
+
TITLE="$(git log -1 --format='%s' "$SHA")"
|
|
91
|
+
BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
92
|
+
jq -n \
|
|
93
|
+
--arg sha "$SHA" \
|
|
94
|
+
--arg shortSha "$SHORT_SHA" \
|
|
95
|
+
--arg branch "$BRANCH" \
|
|
96
|
+
--arg title "$TITLE" \
|
|
97
|
+
--arg buildTime "$BUILD_TIME" \
|
|
98
|
+
'{
|
|
99
|
+
gitSha: $sha,
|
|
100
|
+
gitShortSha: $shortSha,
|
|
101
|
+
gitBranch: $branch,
|
|
102
|
+
commitTitle: $title,
|
|
103
|
+
buildTimeUtc: $buildTime
|
|
104
|
+
}' > BUILD_META.json
|
|
105
|
+
|
|
106
|
+
- name: Publish to GitHub Packages
|
|
107
|
+
if: ${{ inputs.publish-npm }}
|
|
108
|
+
run: |
|
|
109
|
+
echo "//npm.pkg.github.com/:_authToken=${{ github.token }}" > .npmrc
|
|
110
|
+
echo "@shetty4l:registry=https://npm.pkg.github.com" >> .npmrc
|
|
111
|
+
npm publish --access public
|
|
112
|
+
|
|
113
|
+
- name: Create source tarball
|
|
114
|
+
run: |
|
|
115
|
+
TAG="${{ steps.version.outputs.next_tag }}"
|
|
116
|
+
EXTRA_PATHS="${{ inputs.extra-tarball-paths }}"
|
|
117
|
+
tar czf "${{ inputs.service-name }}-${TAG}.tar.gz" \
|
|
118
|
+
--exclude='node_modules' \
|
|
119
|
+
--exclude='.git' \
|
|
120
|
+
--exclude='test' \
|
|
121
|
+
--exclude='.husky' \
|
|
122
|
+
--exclude='.DS_Store' \
|
|
123
|
+
--exclude='dist' \
|
|
124
|
+
src/ \
|
|
125
|
+
package.json \
|
|
126
|
+
bun.lock \
|
|
127
|
+
tsconfig.json \
|
|
128
|
+
biome.json \
|
|
129
|
+
VERSION \
|
|
130
|
+
BUILD_META.json \
|
|
131
|
+
$EXTRA_PATHS
|
|
132
|
+
|
|
133
|
+
- name: Create tag and release
|
|
134
|
+
env:
|
|
135
|
+
GH_TOKEN: ${{ github.token }}
|
|
136
|
+
run: |
|
|
137
|
+
TAG="${{ steps.version.outputs.next_tag }}"
|
|
138
|
+
ASSETS=("${{ inputs.service-name }}-${TAG}.tar.gz")
|
|
139
|
+
|
|
140
|
+
if [ "${{ inputs.attach-install-sh }}" = "true" ] && [ -f "scripts/install.sh" ]; then
|
|
141
|
+
ASSETS+=("scripts/install.sh")
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
git tag "${TAG}"
|
|
145
|
+
git push origin "${TAG}"
|
|
146
|
+
|
|
147
|
+
gh release create "${TAG}" \
|
|
148
|
+
--title "Release ${TAG}" \
|
|
149
|
+
--notes "Automated release ${TAG}" \
|
|
150
|
+
"${ASSETS[@]}"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_run:
|
|
5
|
+
workflows: ["CI"]
|
|
6
|
+
types: [completed]
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
packages: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
release:
|
|
15
|
+
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
|
16
|
+
uses: ./.github/workflows/release-shared.yml
|
|
17
|
+
with:
|
|
18
|
+
service-name: core
|
|
19
|
+
publish-npm: true
|
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# @shetty4l/core
|
|
2
|
+
|
|
3
|
+
Shared infrastructure primitives for Bun/TypeScript services. Zero external dependencies.
|
|
4
|
+
|
|
5
|
+
## Modules
|
|
6
|
+
|
|
7
|
+
| Module | Purpose |
|
|
8
|
+
|--------|---------|
|
|
9
|
+
| `result` | `Result<T, E>` type, `ok`/`err` constructors, `Port` branded type |
|
|
10
|
+
| `version` | Read VERSION file from project root with fallback |
|
|
11
|
+
| `config` | XDG directory resolution, path expansion, env var interpolation, JSON config loading |
|
|
12
|
+
| `cli` | Argument parsing, command dispatch, uptime formatting |
|
|
13
|
+
| `daemon` | PID-file daemon management (start/stop/restart/status) |
|
|
14
|
+
| `http` | Bun.serve wrapper with CORS, health endpoint, JSON response helpers |
|
|
15
|
+
| `signals` | Graceful shutdown handler (SIGINT/SIGTERM) |
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
Configure GitHub Packages registry:
|
|
20
|
+
|
|
21
|
+
```toml
|
|
22
|
+
# bunfig.toml
|
|
23
|
+
[install.scopes]
|
|
24
|
+
"@shetty4l" = "https://npm.pkg.github.com"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
bun add @shetty4l/core
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
### Namespace imports (recommended)
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { config, http, cli, daemon, readVersion, onShutdown } from "@shetty4l/core";
|
|
39
|
+
|
|
40
|
+
const version = readVersion(import.meta.dir);
|
|
41
|
+
const port = config.parsePort(process.env.PORT!, "PORT");
|
|
42
|
+
const cfg = config.loadJsonConfig({ name: "myservice", defaults: { port: 3000 } });
|
|
43
|
+
|
|
44
|
+
const server = http.createServer({
|
|
45
|
+
port: 3000,
|
|
46
|
+
version,
|
|
47
|
+
onRequest: (req, url) => {
|
|
48
|
+
if (url.pathname === "/echo") return http.jsonOk({ ok: true });
|
|
49
|
+
return null; // 404
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
onShutdown(() => server.stop());
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Sub-path imports
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { parsePort, loadJsonConfig } from "@shetty4l/core/config";
|
|
60
|
+
import { createServer, jsonOk } from "@shetty4l/core/http";
|
|
61
|
+
import { createDaemonManager } from "@shetty4l/core/daemon";
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Result type
|
|
65
|
+
|
|
66
|
+
Functions that can fail with expected errors return `Result<T, E>` instead of throwing:
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { config } from "@shetty4l/core";
|
|
70
|
+
|
|
71
|
+
const result = config.parsePort("abc", "PORT");
|
|
72
|
+
if (!result.ok) {
|
|
73
|
+
console.error(result.error);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
// result.value is a branded Port type — validated once, trusted downstream
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Convention:** `Result` for expected failures (invalid input, missing files). `throw` for programmer errors (bugs, invariant violations).
|
|
80
|
+
|
|
81
|
+
## Development
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
bun install
|
|
85
|
+
bun run validate # typecheck + lint + format:check + test
|
|
86
|
+
bun run format # auto-fix formatting
|
|
87
|
+
bun test # run tests only
|
|
88
|
+
```
|
package/biome.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
|
|
3
|
+
"formatter": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"indentStyle": "space",
|
|
6
|
+
"indentWidth": 2
|
|
7
|
+
},
|
|
8
|
+
"linter": {
|
|
9
|
+
"enabled": false
|
|
10
|
+
},
|
|
11
|
+
"assist": { "actions": { "source": { "organizeImports": "on" } } }
|
|
12
|
+
}
|
package/bun.lock
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 1,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "@shetty4l/core",
|
|
7
|
+
"devDependencies": {
|
|
8
|
+
"@biomejs/biome": "^2.3.13",
|
|
9
|
+
"@types/bun": "latest",
|
|
10
|
+
"husky": "^9.0.0",
|
|
11
|
+
"oxlint": "^0.12.0",
|
|
12
|
+
"typescript": "^5.0.0",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
"packages": {
|
|
17
|
+
"@biomejs/biome": ["@biomejs/biome@2.4.2", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.4.2", "@biomejs/cli-darwin-x64": "2.4.2", "@biomejs/cli-linux-arm64": "2.4.2", "@biomejs/cli-linux-arm64-musl": "2.4.2", "@biomejs/cli-linux-x64": "2.4.2", "@biomejs/cli-linux-x64-musl": "2.4.2", "@biomejs/cli-win32-arm64": "2.4.2", "@biomejs/cli-win32-x64": "2.4.2" }, "bin": { "biome": "bin/biome" } }, "sha512-vVE/FqLxNLbvYnFDYg3Xfrh1UdFhmPT5i+yPT9GE2nTUgI4rkqo5krw5wK19YHBd7aE7J6r91RRmb8RWwkjy6w=="],
|
|
18
|
+
|
|
19
|
+
"@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.4.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-3pEcKCP/1POKyaZZhXcxFl3+d9njmeAihZ17k8lL/1vk+6e0Cbf0yPzKItFiT+5Yh6TQA4uKvnlqe0oVZwRxCA=="],
|
|
20
|
+
|
|
21
|
+
"@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.4.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-P7hK1jLVny+0R9UwyGcECxO6sjETxfPyBm/1dmFjnDOHgdDPjPqozByunrwh4xPKld8sxOr5eAsSqal5uKgeBg=="],
|
|
22
|
+
|
|
23
|
+
"@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.4.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-DI3Mi7GT2zYNgUTDEbSjl3e1KhoP76OjQdm8JpvZYZWtVDRyLd3w8llSr2TWk1z+U3P44kUBWY3X7H9MD1/DGQ=="],
|
|
24
|
+
|
|
25
|
+
"@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.4.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-/x04YK9+7erw6tYEcJv9WXoBHcULI/wMOvNdAyE9S3JStZZ9yJyV67sWAI+90UHuDo/BDhq0d96LDqGlSVv7WA=="],
|
|
26
|
+
|
|
27
|
+
"@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.4.2", "", { "os": "linux", "cpu": "x64" }, "sha512-GK2ErnrKpWFigYP68cXiCHK4RTL4IUWhK92AFS3U28X/nuAL5+hTuy6hyobc8JZRSt+upXt1nXChK+tuHHx4mA=="],
|
|
28
|
+
|
|
29
|
+
"@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.4.2", "", { "os": "linux", "cpu": "x64" }, "sha512-wbBmTkeAoAYbOQ33f6sfKG7pcRSydQiF+dTYOBjJsnXO2mWEOQHllKlC2YVnedqZFERp2WZhFUoO7TNRwnwEHQ=="],
|
|
30
|
+
|
|
31
|
+
"@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.4.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-k2uqwLYrNNxnaoiW3RJxoMGnbKda8FuCmtYG3cOtVljs3CzWxaTR+AoXwKGHscC9thax9R4kOrtWqWN0+KdPTw=="],
|
|
32
|
+
|
|
33
|
+
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.4.2", "", { "os": "win32", "cpu": "x64" }, "sha512-9ma7C4g8Sq3cBlRJD2yrsHXB1mnnEBdpy7PhvFrylQWQb4PoyCmPucdX7frvsSBQuFtIiKCrolPl/8tCZrKvgQ=="],
|
|
34
|
+
|
|
35
|
+
"@oxlint/darwin-arm64": ["@oxlint/darwin-arm64@0.12.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UydkjcAImpmBn8JYaMPg0zJrwgWJMGvJagvCnyPfyiBRWAN83Kq+BDgJZgIq+2Te6kvlnoiHWNJKVJmpy0f0BA=="],
|
|
36
|
+
|
|
37
|
+
"@oxlint/darwin-x64": ["@oxlint/darwin-x64@0.12.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-bxLyiAiHzXB56w7cf50YNPpZlK+PMxA8GgHutRSoNK/Z/BR/xsibNLs/9YNUnjHB+PF19+EbIRtJxoHjmbRr8g=="],
|
|
38
|
+
|
|
39
|
+
"@oxlint/linux-arm64-gnu": ["@oxlint/linux-arm64-gnu@0.12.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jVkmfoMjPKFDIZySmpykwrCmx5xhpLJdMpUAR8ycEkFRJFp5qKLWZd6cEjiMb7gxmWN6qcCvDVTF/zEs3aRpyQ=="],
|
|
40
|
+
|
|
41
|
+
"@oxlint/linux-arm64-musl": ["@oxlint/linux-arm64-musl@0.12.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-8VdV1nKYDj7AFaw1a03Ih43/+pUS/hhMZbTFLRMpvlVp1cPtdB77c+bl/OdiJ/BwNTzLIzr/GrospwCoEJkQKg=="],
|
|
42
|
+
|
|
43
|
+
"@oxlint/linux-x64-gnu": ["@oxlint/linux-x64-gnu@0.12.0", "", { "os": "linux", "cpu": "x64" }, "sha512-MacAt8N4XU5DeoHcseXLom/z+B0seecCz8vGAH4ppF2EH49o7NbN7VvFsw2nZ2QNO/4vw+pdS1BHXLTr9lY6zQ=="],
|
|
44
|
+
|
|
45
|
+
"@oxlint/linux-x64-musl": ["@oxlint/linux-x64-musl@0.12.0", "", { "os": "linux", "cpu": "x64" }, "sha512-/ZBDJ9wpUE6bB05nniQl29kD5vJUMg6n75LdHD8F6ThXfsHGI/n7Je3gzggnXokgf9UQpTUPWrWlfEuWVCBMag=="],
|
|
46
|
+
|
|
47
|
+
"@oxlint/win32-arm64": ["@oxlint/win32-arm64@0.12.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-hY1ya9dv8VY8113YSSDfMs/989aFmoA2fIZco8uxTxIEVl9nGY6tDtpgKZqUIiGrrMbDO8BBb1G5jsekmfexbA=="],
|
|
48
|
+
|
|
49
|
+
"@oxlint/win32-x64": ["@oxlint/win32-x64@0.12.0", "", { "os": "win32", "cpu": "x64" }, "sha512-NHLJolo4sZk3nu/bPNuaJ+6p5DdHoRuZAjyuSO6CnLgpmZcYqx7LgngA/x2oB/bLgi4Hv9twjHjODc5Ce5o14g=="],
|
|
50
|
+
|
|
51
|
+
"@types/bun": ["@types/bun@1.3.9", "", { "dependencies": { "bun-types": "1.3.9" } }, "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw=="],
|
|
52
|
+
|
|
53
|
+
"@types/node": ["@types/node@25.2.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ=="],
|
|
54
|
+
|
|
55
|
+
"bun-types": ["bun-types@1.3.9", "", { "dependencies": { "@types/node": "*" } }, "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg=="],
|
|
56
|
+
|
|
57
|
+
"husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="],
|
|
58
|
+
|
|
59
|
+
"oxlint": ["oxlint@0.12.0", "", { "optionalDependencies": { "@oxlint/darwin-arm64": "0.12.0", "@oxlint/darwin-x64": "0.12.0", "@oxlint/linux-arm64-gnu": "0.12.0", "@oxlint/linux-arm64-musl": "0.12.0", "@oxlint/linux-x64-gnu": "0.12.0", "@oxlint/linux-x64-musl": "0.12.0", "@oxlint/win32-arm64": "0.12.0", "@oxlint/win32-x64": "0.12.0" }, "bin": { "oxlint": "bin/oxlint", "oxc_language_server": "bin/oxc_language_server" } }, "sha512-M0vWq8KYtp4vpweRxcdCiVO8QFwzoRyp5bWTMrEL/0Z+GDKCMJltac7H3T3T09FIiktOZLvID733d7OcKk/caw=="],
|
|
60
|
+
|
|
61
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
62
|
+
|
|
63
|
+
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
|
64
|
+
}
|
|
65
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shetty4l/core",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Shared infrastructure primitives for Bun/TypeScript services",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./result": "./src/result.ts",
|
|
10
|
+
"./version": "./src/version.ts",
|
|
11
|
+
"./config": "./src/config.ts",
|
|
12
|
+
"./signals": "./src/signals.ts",
|
|
13
|
+
"./cli": "./src/cli.ts",
|
|
14
|
+
"./daemon": "./src/daemon.ts",
|
|
15
|
+
"./http": "./src/http.ts"
|
|
16
|
+
},
|
|
17
|
+
"bin": {
|
|
18
|
+
"version-bump": "./src/scripts/version-bump.ts"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"lint": "oxlint src/",
|
|
23
|
+
"format": "biome check --write src/ test/",
|
|
24
|
+
"format:check": "biome check src/ test/",
|
|
25
|
+
"test": "bun test",
|
|
26
|
+
"validate": "bun run typecheck && bun run lint && bun run format:check && bun run test",
|
|
27
|
+
"version:bump": "bun run src/scripts/version-bump.ts",
|
|
28
|
+
"prepare": "husky"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@biomejs/biome": "^2.3.13",
|
|
32
|
+
"@types/bun": "latest",
|
|
33
|
+
"husky": "^9.0.0",
|
|
34
|
+
"oxlint": "^0.12.0",
|
|
35
|
+
"typescript": "^5.0.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install-lib.sh — Shared install functions for shetty4l services.
|
|
3
|
+
#
|
|
4
|
+
# Source this file from a per-service install.sh, then call the functions.
|
|
5
|
+
# Required variables before sourcing:
|
|
6
|
+
# SERVICE_NAME — e.g. "engram"
|
|
7
|
+
# REPO — e.g. "shetty4l/engram"
|
|
8
|
+
# INSTALL_BASE — e.g. "$HOME/srv/engram"
|
|
9
|
+
#
|
|
10
|
+
# Optional variables:
|
|
11
|
+
# BIN_DIR — CLI symlink directory (default: $HOME/.local/bin)
|
|
12
|
+
# MAX_VERSIONS — versions to keep (default: 5)
|
|
13
|
+
#
|
|
14
|
+
# After sourcing, these variables are set by fetch_latest_release:
|
|
15
|
+
# RELEASE_TAG — e.g. "v0.2.3"
|
|
16
|
+
# TARBALL_URL — download URL for the release tarball
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
BIN_DIR="${BIN_DIR:-${HOME}/.local/bin}"
|
|
21
|
+
MAX_VERSIONS="${MAX_VERSIONS:-5}"
|
|
22
|
+
|
|
23
|
+
# --- color helpers ---
|
|
24
|
+
|
|
25
|
+
info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
|
|
26
|
+
ok() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
|
|
27
|
+
warn() { printf '\033[1;33m==>\033[0m %s\n' "$*"; }
|
|
28
|
+
err() { printf '\033[1;31m==>\033[0m %s\n' "$*" >&2; }
|
|
29
|
+
die() { err "$@"; exit 1; }
|
|
30
|
+
|
|
31
|
+
# --- prereqs ---
|
|
32
|
+
|
|
33
|
+
check_prereqs() {
|
|
34
|
+
local missing=()
|
|
35
|
+
for cmd in bun curl tar jq; do
|
|
36
|
+
if ! command -v "$cmd" &>/dev/null; then
|
|
37
|
+
missing+=("$cmd")
|
|
38
|
+
fi
|
|
39
|
+
done
|
|
40
|
+
if [ ${#missing[@]} -gt 0 ]; then
|
|
41
|
+
die "Missing required tools: ${missing[*]}"
|
|
42
|
+
fi
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
# --- fetch latest release ---
|
|
46
|
+
|
|
47
|
+
fetch_latest_release() {
|
|
48
|
+
info "Fetching latest release from GitHub..."
|
|
49
|
+
local release_json
|
|
50
|
+
release_json=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest")
|
|
51
|
+
|
|
52
|
+
RELEASE_TAG=$(echo "$release_json" | jq -r '.tag_name')
|
|
53
|
+
TARBALL_URL=$(echo "$release_json" | jq -r ".assets[] | select(.name | startswith(\"${SERVICE_NAME}-\")) | .browser_download_url")
|
|
54
|
+
|
|
55
|
+
if [ -z "$RELEASE_TAG" ] || [ "$RELEASE_TAG" = "null" ]; then
|
|
56
|
+
die "No releases found for ${REPO}"
|
|
57
|
+
fi
|
|
58
|
+
if [ -z "$TARBALL_URL" ] || [ "$TARBALL_URL" = "null" ]; then
|
|
59
|
+
die "No tarball asset found in release ${RELEASE_TAG}"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
info "Latest release: ${RELEASE_TAG}"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# --- download and extract ---
|
|
66
|
+
|
|
67
|
+
download_and_extract() {
|
|
68
|
+
local version_dir="${INSTALL_BASE}/${RELEASE_TAG}"
|
|
69
|
+
|
|
70
|
+
if [ -d "$version_dir" ]; then
|
|
71
|
+
warn "Version ${RELEASE_TAG} already exists at ${version_dir}, reinstalling..."
|
|
72
|
+
rm -rf "$version_dir"
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
mkdir -p "$version_dir"
|
|
76
|
+
|
|
77
|
+
info "Downloading ${RELEASE_TAG}..."
|
|
78
|
+
local tmpfile
|
|
79
|
+
tmpfile=$(mktemp)
|
|
80
|
+
curl -fsSL -o "$tmpfile" "$TARBALL_URL"
|
|
81
|
+
|
|
82
|
+
info "Extracting to ${version_dir}..."
|
|
83
|
+
tar xzf "$tmpfile" -C "$version_dir"
|
|
84
|
+
rm -f "$tmpfile"
|
|
85
|
+
|
|
86
|
+
info "Installing dependencies..."
|
|
87
|
+
(cd "$version_dir" && bun install --frozen-lockfile)
|
|
88
|
+
|
|
89
|
+
info "Creating CLI wrapper..."
|
|
90
|
+
cat > "$version_dir/${SERVICE_NAME}" <<WRAPPER
|
|
91
|
+
#!/usr/bin/env bash
|
|
92
|
+
SCRIPT_DIR="\$(cd "\$(dirname "\$(readlink "\$0" || echo "\$0")")" && pwd)"
|
|
93
|
+
exec bun run "\$SCRIPT_DIR/src/cli.ts" "\$@"
|
|
94
|
+
WRAPPER
|
|
95
|
+
chmod +x "$version_dir/${SERVICE_NAME}"
|
|
96
|
+
|
|
97
|
+
ok "Installed ${RELEASE_TAG} to ${version_dir}"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# --- symlink management (atomic) ---
|
|
101
|
+
|
|
102
|
+
update_symlink() {
|
|
103
|
+
local version_dir="${INSTALL_BASE}/${RELEASE_TAG}"
|
|
104
|
+
local latest_link="${INSTALL_BASE}/latest"
|
|
105
|
+
|
|
106
|
+
ln -sfn "$version_dir" "$latest_link"
|
|
107
|
+
echo "$RELEASE_TAG" > "${INSTALL_BASE}/current-version"
|
|
108
|
+
|
|
109
|
+
ok "Symlinked latest -> ${RELEASE_TAG}"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# --- prune old versions ---
|
|
113
|
+
|
|
114
|
+
prune_versions() {
|
|
115
|
+
local versions=()
|
|
116
|
+
for d in "${INSTALL_BASE}"/v*; do
|
|
117
|
+
[ -d "$d" ] && versions+=("$(basename "$d")")
|
|
118
|
+
done
|
|
119
|
+
|
|
120
|
+
if [ ${#versions[@]} -eq 0 ]; then
|
|
121
|
+
return
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
IFS=$'\n' sorted=($(printf '%s\n' "${versions[@]}" | sed 's/^v//' | sort -t. -k1,1n -k2,2n -k3,3n | sed 's/^/v/'))
|
|
125
|
+
unset IFS
|
|
126
|
+
|
|
127
|
+
local count=${#sorted[@]}
|
|
128
|
+
if [ "$count" -gt "$MAX_VERSIONS" ]; then
|
|
129
|
+
local remove_count=$((count - MAX_VERSIONS))
|
|
130
|
+
for ((i = 0; i < remove_count; i++)); do
|
|
131
|
+
local old_version="${sorted[$i]}"
|
|
132
|
+
info "Removing old version: ${old_version}"
|
|
133
|
+
rm -rf "${INSTALL_BASE}/${old_version}"
|
|
134
|
+
done
|
|
135
|
+
fi
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
# --- CLI binary ---
|
|
139
|
+
|
|
140
|
+
install_cli() {
|
|
141
|
+
mkdir -p "$BIN_DIR"
|
|
142
|
+
ln -sf "${INSTALL_BASE}/latest/${SERVICE_NAME}" "${BIN_DIR}/${SERVICE_NAME}"
|
|
143
|
+
ok "CLI linked: ${BIN_DIR}/${SERVICE_NAME}"
|
|
144
|
+
|
|
145
|
+
if [[ ":$PATH:" != *":${BIN_DIR}:"* ]]; then
|
|
146
|
+
warn "~/.local/bin is not in your PATH. Add it to your shell profile:"
|
|
147
|
+
warn " export PATH=\"\$HOME/.local/bin:\$PATH\""
|
|
148
|
+
fi
|
|
149
|
+
}
|