@eighty4/c2 0.0.1 → 0.0.2-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,8 +4,6 @@ Blow up your `cloud-init` developer workflows!
4
4
 
5
5
  ## Getting started
6
6
 
7
- `c2` publishes only TypeScript requiring Node >=23 to run.
8
-
9
7
  ```shell
10
8
  npm i -g @eighty4/c2
11
9
  c2 -h
@@ -0,0 +1,53 @@
1
+ import { afterEach, beforeEach, expect, test } from 'bun:test'
2
+ import { join } from 'node:path'
3
+ import { collectAttachments } from '#c2/attachments.ts'
4
+ import { makeFile, makeTempDir, removeDir } from '#c2/fs.testing.ts'
5
+
6
+ let tmpDir: string
7
+
8
+ beforeEach(async () => (tmpDir = await makeTempDir()))
9
+
10
+ afterEach(async () => await removeDir(tmpDir))
11
+
12
+ test('collect cloud config yml', async () => {
13
+ await makeFile('init-cloud.yml', 'whoopie', tmpDir)
14
+ expect(await collectAttachments(tmpDir)).toStrictEqual([
15
+ {
16
+ path: join(tmpDir, 'init-cloud.yml'),
17
+ content: 'whoopie',
18
+ filename: 'init-cloud.yml',
19
+ type: 'cloud-config',
20
+ },
21
+ ])
22
+ })
23
+
24
+ test('collect shell script', async () => {
25
+ await makeFile('init-cloud.sh', 'whoopie', tmpDir)
26
+ expect(await collectAttachments(tmpDir)).toStrictEqual([
27
+ {
28
+ path: join(tmpDir, 'init-cloud.sh'),
29
+ content: 'whoopie',
30
+ filename: 'init-cloud.sh',
31
+ type: 'x-shellscript',
32
+ },
33
+ ])
34
+ })
35
+
36
+ test('sorts attachments by filename', async () => {
37
+ await makeFile('01-init-cloud.sh', 'whoopie', tmpDir)
38
+ await makeFile('02-init-cloud.sh', 'whoopie', tmpDir)
39
+ expect(await collectAttachments(tmpDir)).toStrictEqual([
40
+ {
41
+ path: join(tmpDir, '01-init-cloud.sh'),
42
+ content: 'whoopie',
43
+ filename: '01-init-cloud.sh',
44
+ type: 'x-shellscript',
45
+ },
46
+ {
47
+ path: join(tmpDir, '02-init-cloud.sh'),
48
+ content: 'whoopie',
49
+ filename: '02-init-cloud.sh',
50
+ type: 'x-shellscript',
51
+ },
52
+ ])
53
+ })
@@ -0,0 +1,93 @@
1
+ import { afterEach, beforeEach, expect, test } from 'bun:test'
2
+ import { buildUserData } from '#c2/build.ts'
3
+ import { makeFile, makeTempDir, removeDir } from '#c2/fs.testing.ts'
4
+
5
+ let tmpDir: string
6
+
7
+ beforeEach(async () => {
8
+ tmpDir = await makeTempDir()
9
+ })
10
+
11
+ afterEach(async () => removeDir(tmpDir))
12
+
13
+ test('build user data of single file', async () => {
14
+ const initCloudYml = 'whoopie'
15
+ await makeFile('init-cloud.yml', initCloudYml, tmpDir)
16
+ expect(await buildUserData(tmpDir)).toStrictEqual('whoopie')
17
+ })
18
+
19
+ test('build user data of single file with template expression', async () => {
20
+ const whoopie = await makeTempDir()
21
+ await makeFile('whoopie', 'whoopie', whoopie)
22
+ await makeFile(
23
+ 'init-cloud.yml',
24
+ `\${{ file('${whoopie}/whoopie')}}`,
25
+ tmpDir,
26
+ )
27
+ expect(await buildUserData(tmpDir)).toStrictEqual('whoopie')
28
+ await removeDir(whoopie)
29
+ })
30
+
31
+ test('build user data multipart message', async () => {
32
+ await makeFile('1-init-cloud.yml', 'whoopie', tmpDir)
33
+ await makeFile('2-init-cloud.sh', 'cushion', tmpDir)
34
+ const boundary = Bun.randomUUIDv7()
35
+ expect(
36
+ await buildUserData(tmpDir, { attachmentBoundary: boundary }),
37
+ ).toStrictEqual(
38
+ `Content-Type: multipart/mixed; boundary=${boundary}
39
+ MIME-Version: 1.0
40
+ Number-Attachments: 2
41
+ --${boundary}
42
+ Content-Type: text/cloud-config; charset="us-ascii"
43
+ MIME-Version: 1.0
44
+ Content-Transfer-Encoding: 7bit
45
+ Content-Disposition: attachment; filename="1-init-cloud.yml"
46
+
47
+ whoopie
48
+ --${boundary}
49
+ Content-Type: text/x-shellscript; charset="us-ascii"
50
+ MIME-Version: 1.0
51
+ Content-Transfer-Encoding: 7bit
52
+ Content-Disposition: attachment; filename="2-init-cloud.sh"
53
+
54
+ cushion
55
+ --${boundary}
56
+ `,
57
+ )
58
+ })
59
+
60
+ test('build user data multipart with template expression', async () => {
61
+ const whoopie = await makeTempDir()
62
+ await makeFile('whoopie', 'whoopie', whoopie)
63
+ await makeFile(
64
+ '1-init-cloud.yml',
65
+ `\${{ file('${whoopie}/whoopie')}}`,
66
+ tmpDir,
67
+ )
68
+ await makeFile('2-init-cloud.sh', 'cushion', tmpDir)
69
+ const boundary = Bun.randomUUIDv7()
70
+ expect(
71
+ await buildUserData(tmpDir, { attachmentBoundary: boundary }),
72
+ ).toStrictEqual(
73
+ `Content-Type: multipart/mixed; boundary=${boundary}
74
+ MIME-Version: 1.0
75
+ Number-Attachments: 2
76
+ --${boundary}
77
+ Content-Type: text/cloud-config; charset="us-ascii"
78
+ MIME-Version: 1.0
79
+ Content-Transfer-Encoding: 7bit
80
+ Content-Disposition: attachment; filename="1-init-cloud.yml"
81
+
82
+ whoopie
83
+ --${boundary}
84
+ Content-Type: text/x-shellscript; charset="us-ascii"
85
+ MIME-Version: 1.0
86
+ Content-Transfer-Encoding: 7bit
87
+ Content-Disposition: attachment; filename="2-init-cloud.sh"
88
+
89
+ cushion
90
+ --${boundary}
91
+ `,
92
+ )
93
+ })
package/lib/c2.api.ts ADDED
@@ -0,0 +1 @@
1
+ export { type BuildUserDataOpts, buildUserData } from '#c2/build.ts'
@@ -0,0 +1,70 @@
1
+ import { expect, test } from 'bun:test'
2
+ import { parseArgs } from '#c2/cli.ts'
3
+
4
+ test('parseArgs', () => {
5
+ expect(
6
+ parseArgs([
7
+ '/Users/who/.bun/bin/bun',
8
+ '/Users/who/user-data/c2.ts',
9
+ 'user_data_dir',
10
+ ]),
11
+ ).toStrictEqual({
12
+ base64: false,
13
+ userDataDir: 'user_data_dir',
14
+ })
15
+ })
16
+
17
+ test('parseArgs errors without USER_DATA_DIR', () => {
18
+ expect(() =>
19
+ parseArgs(['/Users/who/.bun/bin/bun', '/Users/who/user-data/c2.ts']),
20
+ ).toThrow()
21
+ })
22
+
23
+ test('parseArgs errors with extra USER_DATA_DIR', () => {
24
+ expect(() =>
25
+ parseArgs([
26
+ '/Users/who/.bun/bin/bun',
27
+ '/Users/who/user-data/c2.ts',
28
+ 'user_data_dir',
29
+ 'some_other_arg',
30
+ ]),
31
+ ).toThrow('')
32
+ })
33
+
34
+ test('parseArgs with --base64', () => {
35
+ expect(
36
+ parseArgs([
37
+ '/Users/who/.bun/bin/bun',
38
+ '/Users/who/user-data/c2.ts',
39
+ '--base64',
40
+ 'user_data_dir',
41
+ ]),
42
+ ).toStrictEqual({
43
+ base64: true,
44
+ userDataDir: 'user_data_dir',
45
+ })
46
+ })
47
+
48
+ test('parseArgs with --http PORT', () => {
49
+ expect(
50
+ parseArgs([
51
+ '/Users/who/.bun/bin/bun',
52
+ '/Users/who/user-data/c2.ts',
53
+ '--http',
54
+ '6666',
55
+ 'user_data_dir',
56
+ ]),
57
+ ).toStrictEqual({ httpPort: 6666, userDataDir: 'user_data_dir' })
58
+ })
59
+
60
+ test('parseArgs with --http bunk', () => {
61
+ expect(() =>
62
+ parseArgs([
63
+ '/Users/who/.bun/bin/bun',
64
+ '/Users/who/user-data/c2.ts',
65
+ '--http',
66
+ 'bunk',
67
+ 'user_data_dir',
68
+ ]),
69
+ ).toThrow('--http bunk is not a valid http port')
70
+ })
@@ -0,0 +1,74 @@
1
+ import { afterAll, afterEach, beforeEach, expect, test } from 'bun:test'
2
+ import { join } from 'node:path'
3
+ import { evalTemplateExpressions } from '#c2/expression.ts'
4
+ import { makeFile, makeTempDir, removeDir } from '#c2/fs.testing.ts'
5
+
6
+ let files: Array<string> = []
7
+ let tmpDir: string
8
+
9
+ beforeEach(async () => (tmpDir = await makeTempDir()))
10
+
11
+ afterEach(async () => await removeDir(tmpDir))
12
+
13
+ afterAll(async () => {
14
+ for (const p of files) {
15
+ await Bun.file(p).delete()
16
+ }
17
+ })
18
+
19
+ test('env() found', async () => {
20
+ const envVar = 'C2_TEST_' + Bun.randomUUIDv7().substring(0, 8).toUpperCase()
21
+ Bun.env[envVar] = Bun.randomUUIDv7()
22
+ expect(await evalTemplateExpressions(`\${{ env('${envVar}') }}`)).toBe(
23
+ Bun.env[envVar],
24
+ )
25
+ delete Bun.env[envVar]
26
+ })
27
+
28
+ test('env() not found', async () => {
29
+ const envVar = 'C2_TEST_' + Bun.randomUUIDv7().substring(0, 8).toUpperCase()
30
+ expect(() => evalTemplateExpressions(` \${{ env('${envVar}') }}`)).toThrow(
31
+ `env var \`${envVar}\` does not exist`,
32
+ )
33
+ })
34
+
35
+ test('file() not found', async () => {
36
+ const path = join(await makeTempDir(), 'whoopie')
37
+ expect(() => evalTemplateExpressions(`\${{ file('${path}') }}`)).toThrow(
38
+ `ENOENT: no such file or directory, open '${path}'`,
39
+ )
40
+ })
41
+
42
+ test('file() absolute', async () => {
43
+ const content = Bun.randomUUIDv7()
44
+ const path = await makeFile('.user-data', content, tmpDir)
45
+ expect(await evalTemplateExpressions(`\${{ file('${path}') }}`)).toBe(
46
+ content,
47
+ )
48
+ })
49
+
50
+ test('file() home', async () => {
51
+ const content = Bun.randomUUIDv7()
52
+ const filename = `.user-data.${Bun.randomUUIDv7()}`
53
+ const path = join(Bun.env.HOME!, filename)
54
+ await makeFile(path, content)
55
+ files.push(path)
56
+ expect(await evalTemplateExpressions(`\${{ file('~/${filename}') }}`)).toBe(
57
+ content,
58
+ )
59
+ })
60
+
61
+ test('file() relative', async () => {
62
+ const content = Bun.randomUUIDv7()
63
+ const filename = `.user-data.${Bun.randomUUIDv7()}`
64
+ await makeFile(filename, content, tmpDir)
65
+ const prevPwd = process.cwd()
66
+ process.chdir(tmpDir)
67
+ try {
68
+ expect(
69
+ await evalTemplateExpressions(`\${{ file('${filename}') }}`),
70
+ ).toBe(content)
71
+ } finally {
72
+ process.chdir(prevPwd)
73
+ }
74
+ })
@@ -0,0 +1,21 @@
1
+ import { mkdtemp, rm } from 'node:fs/promises'
2
+ import { tmpdir } from 'node:os'
3
+ import { join } from 'node:path'
4
+
5
+ export async function makeFile(
6
+ path: string,
7
+ content: string,
8
+ pathPrefix?: string,
9
+ ): Promise<string> {
10
+ const p = !!pathPrefix ? join(pathPrefix, path) : path
11
+ await Bun.file(p).write(content)
12
+ return p
13
+ }
14
+
15
+ export async function makeTempDir(): Promise<string> {
16
+ return await mkdtemp(join(tmpdir(), 'c2-test-'))
17
+ }
18
+
19
+ export async function removeDir(p: string): Promise<void> {
20
+ await rm(p, { force: true, recursive: true })
21
+ }
@@ -0,0 +1,31 @@
1
+ import { readDirListing, readToString } from '#c2/fs.ts';
2
+ export async function collectAttachments(dir) {
3
+ const result = [];
4
+ for (const filename of await readDirListing(dir)) {
5
+ const path = `${dir}/${filename}`;
6
+ const content = await readToString(path);
7
+ const type = resolveAttachmentType(filename, content);
8
+ result.push({ content, filename, path, type });
9
+ }
10
+ return result.sort(compareAttachmentFilenames);
11
+ }
12
+ function compareAttachmentFilenames(a1, a2) {
13
+ if (a1.filename === a2.filename)
14
+ return 0;
15
+ if (a1.filename > a2.filename)
16
+ return 1;
17
+ return -1;
18
+ }
19
+ export function resolveAttachmentType(filename, content) {
20
+ if (filename.endsWith('.yml') ||
21
+ (filename.endsWith('.yaml') &&
22
+ content.trim().startsWith('#cloud-config'))) {
23
+ return 'cloud-config';
24
+ }
25
+ else if (filename.endsWith('.sh')) {
26
+ return 'x-shellscript';
27
+ }
28
+ else {
29
+ throw new Error(`unsupported file type ${filename}`);
30
+ }
31
+ }
@@ -0,0 +1,42 @@
1
+ import { collectAttachments } from '#c2/attachments.ts';
2
+ import { evalTemplateExpressions } from '#c2/expression.ts';
3
+ import { readToString } from '#c2/fs.ts';
4
+ export async function buildUserData(userDataDir, opts) {
5
+ const attachments = await collectAttachments(userDataDir);
6
+ switch (attachments.length) {
7
+ case 0:
8
+ throw new Error(`nothing found in dir ${userDataDir}`);
9
+ case 1:
10
+ return evalTemplateExpressions(await readToString(attachments[0].path));
11
+ default:
12
+ return buildMultipartUserData(attachments, opts?.attachmentBoundary);
13
+ }
14
+ }
15
+ function createBoundary() {
16
+ return new Date().toISOString();
17
+ }
18
+ async function buildMultipartUserData(attachments, boundary = createBoundary()) {
19
+ let result = `Content-Type: multipart/mixed; boundary=${boundary}
20
+ MIME-Version: 1.0
21
+ Number-Attachments: ${attachments.length}
22
+ --${boundary}
23
+ `;
24
+ for (const attachment of attachments) {
25
+ let content;
26
+ try {
27
+ content = await evalTemplateExpressions(attachment.content);
28
+ }
29
+ catch (e) {
30
+ throw new Error(`error templating ${attachment.filename}: ${e.message}`);
31
+ }
32
+ result += `Content-Type: text/${attachment.type}; charset="us-ascii"
33
+ MIME-Version: 1.0
34
+ Content-Transfer-Encoding: 7bit
35
+ Content-Disposition: attachment; filename="${attachment.filename}"
36
+
37
+ ${content}
38
+ --${boundary}
39
+ `;
40
+ }
41
+ return result;
42
+ }
@@ -0,0 +1 @@
1
+ export { buildUserData } from '#c2/build.ts';
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ import { buildUserData } from '#c2/build.ts';
3
+ import { parseArgs } from '#c2/cli.ts';
4
+ import { doesDirExist } from '#c2/fs.ts';
5
+ let args;
6
+ try {
7
+ args = parseArgs();
8
+ }
9
+ catch (e) {
10
+ if (e.message) {
11
+ console.error(e.message);
12
+ }
13
+ }
14
+ if (!args || args.help) {
15
+ const optional = (s) => `\u001b[90m${s}\u001b[0m`;
16
+ const required = (s) => `\u001b[1m${s}\u001b[0m`;
17
+ errorExit(`c2 ${optional('[[--base64] | [--http PORT]]')} ${required('USER_DATA_DIR')}`);
18
+ }
19
+ if (args.httpPort) {
20
+ errorExit('--http PORT is not yet implemented');
21
+ }
22
+ if (!(await doesDirExist(args.userDataDir))) {
23
+ errorExit(`${args.userDataDir} directory does not exist`);
24
+ }
25
+ try {
26
+ const userData = await buildUserData(args.userDataDir);
27
+ console.log(args.base64 ? btoa(userData) : userData);
28
+ }
29
+ catch (e) {
30
+ errorExit(e.message);
31
+ }
32
+ function errorExit(msg) {
33
+ console.error(msg);
34
+ process.exit(1);
35
+ }
package/lib_js/cli.js ADDED
@@ -0,0 +1,44 @@
1
+ export function parseArgs(args) {
2
+ if (!args) {
3
+ args = process.argv;
4
+ }
5
+ args = [...args];
6
+ while (!args.shift().endsWith('/c2.ts')) { }
7
+ let base64 = false;
8
+ let httpPort;
9
+ let userData = [];
10
+ let expectHttpPort = false;
11
+ for (const arg of args) {
12
+ if (expectHttpPort) {
13
+ expectHttpPort = false;
14
+ httpPort = parseInt(arg, 10);
15
+ if (isNaN(httpPort)) {
16
+ throw new Error(`--http ${arg} is not a valid http port`);
17
+ }
18
+ }
19
+ else if (arg === '-h' || arg === '--help') {
20
+ return { help: true };
21
+ }
22
+ else if (arg === '--base64') {
23
+ base64 = true;
24
+ }
25
+ else if (arg === '--http') {
26
+ expectHttpPort = true;
27
+ }
28
+ else {
29
+ userData.push(arg);
30
+ }
31
+ }
32
+ switch (userData.length) {
33
+ case 1:
34
+ const userDataDir = userData[0];
35
+ if (typeof httpPort !== 'undefined') {
36
+ return { httpPort, userDataDir };
37
+ }
38
+ else {
39
+ return { base64, userDataDir };
40
+ }
41
+ default:
42
+ throw new Error();
43
+ }
44
+ }
@@ -0,0 +1,47 @@
1
+ import MagicString from 'magic-string';
2
+ import { readToString } from '#c2/fs.ts';
3
+ export async function evalTemplateExpressions(content) {
4
+ const regex = new RegExp(/\${{\s*(.*)\s*}}/g);
5
+ let match;
6
+ const expressions = [];
7
+ while ((match = regex.exec(content)) != null) {
8
+ expressions.push({
9
+ index: match.index,
10
+ innie: match[1],
11
+ outie: match[0],
12
+ });
13
+ }
14
+ if (!expressions.length) {
15
+ return content;
16
+ }
17
+ const ms = new MagicString(content);
18
+ for (const expression of expressions) {
19
+ ms.update(expression.index, expression.index + expression.outie.length, await evaluate(expression.innie));
20
+ }
21
+ return ms.toString();
22
+ }
23
+ async function evaluate(expression) {
24
+ let match;
25
+ if ((match = expression.match(/env\(\s*'(.*)'\s*\)/)) != null) {
26
+ const envVarKey = match[1];
27
+ if (!/[A-Z_]+/.test(envVarKey)) {
28
+ throw new Error(`env var expression \`${envVarKey}\` is not valid syntax`);
29
+ }
30
+ const envVarValue = process.env[envVarKey];
31
+ if (!envVarValue) {
32
+ throw new Error(`env var \`${envVarKey}\` does not exist`);
33
+ }
34
+ return envVarValue;
35
+ }
36
+ else if ((match = expression.match(/file\(\s*'(.*)'\s*\)/)) != null) {
37
+ let path = match[1];
38
+ if (path.startsWith('~/')) {
39
+ if (!process.env.HOME) {
40
+ throw new Error(`file \`${path}\` cannot be resolved without env var HOME`);
41
+ }
42
+ path = `${process.env.HOME}${path.substring(1)}`;
43
+ }
44
+ return readToString(path);
45
+ }
46
+ throw new Error(`unsupported expression: ${expression}`);
47
+ }
package/lib_js/fs.js ADDED
@@ -0,0 +1,15 @@
1
+ import { readdir, readFile, stat } from 'node:fs/promises';
2
+ export async function doesDirExist(p) {
3
+ try {
4
+ return (await stat(p)).isDirectory();
5
+ }
6
+ catch (ignore) {
7
+ return false;
8
+ }
9
+ }
10
+ export async function readDirListing(p) {
11
+ return readdir(p);
12
+ }
13
+ export async function readToString(p) {
14
+ return readFile(p, 'utf8');
15
+ }
@@ -0,0 +1,14 @@
1
+ import { mkdtemp, rm } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ export async function makeFile(path, content, pathPrefix) {
5
+ const p = !!pathPrefix ? join(pathPrefix, path) : path;
6
+ await Bun.file(p).write(content);
7
+ return p;
8
+ }
9
+ export async function makeTempDir() {
10
+ return await mkdtemp(join(tmpdir(), 'c2-test-'));
11
+ }
12
+ export async function removeDir(p) {
13
+ await rm(p, { force: true, recursive: true });
14
+ }
@@ -0,0 +1,10 @@
1
+ export type AttachmentType = 'cloud-config' | 'x-shellscript';
2
+ export interface Attachment {
3
+ path: string;
4
+ content: string;
5
+ filename: string;
6
+ type: AttachmentType;
7
+ }
8
+ export declare function collectAttachments(dir: string): Promise<Array<Attachment>>;
9
+ export declare function resolveAttachmentType(filename: string, content: string): AttachmentType;
10
+ //# sourceMappingURL=attachments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attachments.d.ts","sourceRoot":"","sources":["../lib/attachments.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,eAAe,CAAA;AAE7D,MAAM,WAAW,UAAU;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,cAAc,CAAA;CACvB;AAED,wBAAsB,kBAAkB,CACpC,GAAG,EAAE,MAAM,GACZ,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAS5B;AAWD,wBAAgB,qBAAqB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAChB,cAAc,CAYhB"}
@@ -0,0 +1,5 @@
1
+ export type BuildUserDataOpts = {
2
+ attachmentBoundary?: string;
3
+ };
4
+ export declare function buildUserData(userDataDir: string, opts?: BuildUserDataOpts): Promise<string>;
5
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../lib/build.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC9B,CAAA;AAED,wBAAsB,aAAa,CAC/B,WAAW,EAAE,MAAM,EACnB,IAAI,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,MAAM,CAAC,CAYjB"}
@@ -0,0 +1,2 @@
1
+ export { type BuildUserDataOpts, buildUserData } from '#c2/build.ts';
2
+ //# sourceMappingURL=c2.api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"c2.api.d.ts","sourceRoot":"","sources":["../lib/c2.api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=c2.bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"c2.bin.d.ts","sourceRoot":"","sources":["../lib/c2.bin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ export type ParsedArgs = {
2
+ help: true;
3
+ } | {
4
+ help?: false;
5
+ base64?: boolean;
6
+ httpPort?: number;
7
+ userDataDir: string;
8
+ };
9
+ export declare function parseArgs(args?: Array<string>): ParsedArgs;
10
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../lib/cli.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAChB;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,GACd;IACI,IAAI,CAAC,EAAE,KAAK,CAAA;IACZ,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACtB,CAAA;AAEP,wBAAgB,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,UAAU,CAsC1D"}
@@ -0,0 +1,2 @@
1
+ export declare function evalTemplateExpressions(content: string): Promise<string>;
2
+ //# sourceMappingURL=expression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expression.d.ts","sourceRoot":"","sources":["../lib/expression.ts"],"names":[],"mappings":"AASA,wBAAsB,uBAAuB,CACzC,OAAO,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAuBjB"}
@@ -0,0 +1,4 @@
1
+ export declare function doesDirExist(p: string): Promise<boolean>;
2
+ export declare function readDirListing(p: string): Promise<Array<string>>;
3
+ export declare function readToString(p: string): Promise<string>;
4
+ //# sourceMappingURL=fs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../lib/fs.ts"],"names":[],"mappings":"AAEA,wBAAsB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM9D;AAED,wBAAsB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAEtE;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE7D"}
@@ -0,0 +1,4 @@
1
+ export declare function makeFile(path: string, content: string, pathPrefix?: string): Promise<string>;
2
+ export declare function makeTempDir(): Promise<string>;
3
+ export declare function removeDir(p: string): Promise<void>;
4
+ //# sourceMappingURL=fs.testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.testing.d.ts","sourceRoot":"","sources":["../lib/fs.testing.ts"],"names":[],"mappings":"AAIA,wBAAsB,QAAQ,CAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAEnD;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAExD"}
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "@eighty4/c2",
3
- "version": "0.0.1",
3
+ "version": "0.0.2-1",
4
4
  "author": "Adam McKee <adam.be.g84d@gmail.com>",
5
5
  "repository": "https://github.com/eighty4/c2",
6
6
  "homepage": "https://github.com/eighty4/c2",
7
7
  "keywords": [
8
8
  "devops",
9
- "cloud init",
9
+ "cloud-init",
10
10
  "cloud config",
11
- "user data"
11
+ "user data",
12
+ "linux",
13
+ "cloud"
12
14
  ],
13
15
  "description": "Cross platform cloud config tooling for cloud-init",
14
16
  "license": "BSD-2-Clause",
@@ -17,20 +19,19 @@
17
19
  "bun": ">=1.2",
18
20
  "node": ">=23"
19
21
  },
20
- "main": "lib/build.ts",
21
22
  "bin": {
22
- "c2": "./c2.ts"
23
+ "c2": "./lib_js/c2.bin.js"
23
24
  },
25
+ "main": "./lib_js/c2.api.js",
26
+ "imports": {
27
+ "#c2/*.js": "./lib_js/*.js",
28
+ "#c2/*.ts": "./lib/*.ts"
29
+ },
30
+ "types": "./lib_types",
24
31
  "scripts": {
32
+ "build": "tsc",
25
33
  "fmtcheck": "prettier --check .",
26
- "typecheck": "tsc"
27
- },
28
- "imports": {
29
- "#c2/fs.ts": {
30
- "bun": "./lib/fs.bun.ts",
31
- "node": "./lib/fs.node.ts"
32
- },
33
- "#c2/*": "./lib/*"
34
+ "typecheck": "tsc --noEmit"
34
35
  },
35
36
  "dependencies": {
36
37
  "magic-string": "0.30.17"
@@ -42,10 +43,9 @@
42
43
  "typescript": "5.8.2"
43
44
  },
44
45
  "files": [
45
- "lib/**/*.ts",
46
- "!lib/**/*.spec.ts",
47
- "!lib/fs.testing.ts",
48
- "CHANGELOG.md",
49
- "c2.ts"
46
+ "lib/*",
47
+ "lib_js/*",
48
+ "lib_types/*",
49
+ "CHANGELOG.md"
50
50
  ]
51
51
  }
package/lib/fs.bun.ts DELETED
@@ -1,5 +0,0 @@
1
- export { doesDirExist, readDirListing } from '#c2/fs.node.ts'
2
-
3
- export async function readToString(p: string): Promise<string> {
4
- return Bun.file(p).text()
5
- }
File without changes
File without changes