@ibgib/space-gib 0.0.9 → 0.0.13

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 (82) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -0
  3. package/dist/client/bootstrap.mjs +33 -33
  4. package/dist/client/bootstrap.mjs.map +3 -3
  5. package/dist/client/chunk-ROJZUPPE.mjs +19 -0
  6. package/dist/client/chunk-ROJZUPPE.mjs.map +7 -0
  7. package/dist/client/chunk-WJHRARK6.mjs +2329 -0
  8. package/dist/client/chunk-WJHRARK6.mjs.map +7 -0
  9. package/dist/client/index.mjs +22 -1
  10. package/dist/client/index.mjs.map +4 -4
  11. package/dist/client/script.mjs +1 -1
  12. package/dist/server/server.mjs +19147 -18210
  13. package/dist/server/server.mjs.map +4 -4
  14. package/package.json +4 -4
  15. package/space-gib.localhost-1785337158142.log +75 -0
  16. package/src/client/AUTO-GENERATED-version.mts +1 -1
  17. package/src/client/README.md +1 -1
  18. package/src/client/api/space-gib-api-bridge.mts +8 -224
  19. package/src/client/ui/shell/space-gib-shell-service.mts +68 -18
  20. package/src/common/keystone-policies.mts +1 -1
  21. package/src/server/README.md +3 -2
  22. package/src/server/server.mts +13 -32
  23. package/src/server/test.json +0 -0
  24. package/dist/client/chunk-ACIGPCQW.mjs +0 -2407
  25. package/dist/client/chunk-ACIGPCQW.mjs.map +0 -7
  26. package/dist/client/chunk-QRMTJE6M.mjs +0 -42
  27. package/dist/client/chunk-QRMTJE6M.mjs.map +0 -7
  28. package/space-gib.localhost-1783713259760.log +0 -45
  29. package/src/client/components/identity-header/IMPLEMENTATION.md +0 -45
  30. package/src/client/components/identity-header/identity-header.css +0 -112
  31. package/src/client/components/identity-header/identity-header.html +0 -11
  32. package/src/client/components/identity-header/identity-header.mts +0 -439
  33. package/src/client/components/identity-manager/IMPLEMENTATION.md +0 -100
  34. package/src/client/components/identity-manager/identity-manager.css +0 -171
  35. package/src/client/components/identity-manager/identity-manager.html +0 -19
  36. package/src/client/components/identity-manager/identity-manager.mts +0 -639
  37. package/src/client/components/keystone-creator/keystone-creator.css +0 -65
  38. package/src/client/components/keystone-creator/keystone-creator.html +0 -41
  39. package/src/client/components/keystone-creator/keystone-creator.mts +0 -443
  40. package/src/client/components/keystone-details/keystone-details.css +0 -611
  41. package/src/client/components/keystone-details/keystone-details.html +0 -151
  42. package/src/client/components/keystone-details/keystone-details.mts +0 -1213
  43. package/src/client/components/keystone-scrubber/SCRUBBER_IMPLEMENTATION.md +0 -33
  44. package/src/client/components/keystone-scrubber/keystone-scrubber.css +0 -46
  45. package/src/client/components/keystone-scrubber/keystone-scrubber.html +0 -15
  46. package/src/client/components/keystone-scrubber/keystone-scrubber.mts +0 -356
  47. package/src/server/bootstrap-helper.mts +0 -141
  48. package/src/server/bootstrap-helper.respec.mts +0 -100
  49. package/src/server/metaspace-nodeindexedspace/metaspace-nodeindexedspace.mts +0 -85
  50. package/src/server/path-constants.mts +0 -101
  51. package/src/server/path-helper.mts +0 -101
  52. package/src/server/path-helper.respec.mts +0 -99
  53. package/src/server/serve-gib/CHANGELOG.md +0 -29
  54. package/src/server/serve-gib/README.md +0 -43
  55. package/src/server/serve-gib/constants.mts +0 -1
  56. package/src/server/serve-gib/handlers/api/debug/ws-echo.handler.mts +0 -105
  57. package/src/server/serve-gib/handlers/api/health.handler.mts +0 -23
  58. package/src/server/serve-gib/handlers/api/health.respec.mts +0 -51
  59. package/src/server/serve-gib/handlers/api/ibgib/ibgib-handler-types.mts +0 -49
  60. package/src/server/serve-gib/handlers/api/ibgib/ibgib.handler.mts +0 -176
  61. package/src/server/serve-gib/handlers/api/keystone/keystone-evolve.handler.mts +0 -150
  62. package/src/server/serve-gib/handlers/api/keystone/keystone-genesis.handler.mts +0 -146
  63. package/src/server/serve-gib/handlers/api/keystone/keystone-get.handler.mts +0 -198
  64. package/src/server/serve-gib/handlers/api/keystone/keystone-get.respec.mts +0 -107
  65. package/src/server/serve-gib/handlers/api/keystone/keystone-handler-types.mts +0 -29
  66. package/src/server/serve-gib/handlers/api/keystone/keystone-post.handler.mts +0 -55
  67. package/src/server/serve-gib/handlers/api/keystone/keystone-post.respec.mts +0 -130
  68. package/src/server/serve-gib/handlers/api/keystone/sso-config.handler.mts +0 -66
  69. package/src/server/serve-gib/handlers/api/keystone/sso-link.handler.mts +0 -140
  70. package/src/server/serve-gib/handlers/api/keystone/sso-login.handler.mts +0 -190
  71. package/src/server/serve-gib/handlers/error-handler.mts +0 -36
  72. package/src/server/serve-gib/handlers/handler-base.mts +0 -383
  73. package/src/server/serve-gib/handlers/static-handler.mts +0 -82
  74. package/src/server/serve-gib/handlers/ws/sync-upgrade-handler-base.mts +0 -233
  75. package/src/server/serve-gib/handlers/ws/sync-upgrade.handler.mts +0 -24
  76. package/src/server/serve-gib/handlers/ws/ws-helper.mts +0 -216
  77. package/src/server/serve-gib/handlers/ws/ws-types.mts +0 -53
  78. package/src/server/serve-gib/serve-gib-helpers.mts +0 -32
  79. package/src/server/serve-gib/serve-gib-v1.mts +0 -172
  80. package/src/server/serve-gib/serve-gib.respec.mts +0 -90
  81. package/src/server/serve-gib/types.mts +0 -102
  82. package/src/server/server-constants.mts +0 -2
@@ -1,100 +0,0 @@
1
- import { join } from 'node:path';
2
- import { existsSync, mkdirSync, rmSync } from 'node:fs';
3
-
4
- import { respecfully, iReckon, ifWe } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
5
- const sir = `[${import.meta.url}]`;
6
- import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
7
- import { KeystoneService_V1 } from '@ibgib/core-gib/dist/keystone/keystone-service-v1.mjs';
8
- import { createStandardPoolConfig } from '@ibgib/core-gib/dist/keystone/keystone-config-builder.mjs';
9
- import { POOL_ID_DEFAULT } from '@ibgib/core-gib/dist/keystone/keystone-constants.mjs';
10
-
11
- import { GLOBAL_LOG_A_LOT } from './server-constants.mjs';
12
- import { getDomainRootPath, bootstrapDomainMetaspace } from './bootstrap-helper.mjs';
13
-
14
- const logalot = GLOBAL_LOG_A_LOT;
15
-
16
-
17
- /**
18
- * Minimal mock to satisfy KeystoneService requirements during genesis.
19
- */
20
- class MockMetaspace {
21
- async getLocalUserSpace() { return new MockSpace(); }
22
- async put() { }
23
- async registerNewIbGib() { }
24
- }
25
- class MockSpace {
26
- async witness() { return { data: { success: true } }; }
27
- }
28
-
29
- await respecfully(sir, 'bootstrap-helper', async () => {
30
- const lc = `[bootstrap-helper.respec.mts][bootstrap-helper]`;
31
- const testDataDir = './test-data';
32
- console.log(`${lc} testDataDir: ${testDataDir} (I: c9f39853a57a5af97ab8ff7882c49826)`);
33
-
34
- // Clean and recreate test-data directory
35
- if (existsSync(testDataDir)) {
36
- rmSync(testDataDir, { recursive: true, force: true });
37
- }
38
- mkdirSync(testDataDir, { recursive: true });
39
-
40
- await ifWe(sir, 'calculate balanced domain root paths', async () => {
41
- const tjpGib = 'abcde1234567890';
42
- const root = getDomainRootPath(tjpGib, testDataDir);
43
-
44
- // Should be: testDataDir/domains/ab/cd/abcde1234567890
45
- iReckon(sir, root).includes(join('domains', 'ab', 'cd', tjpGib));
46
- });
47
-
48
- await ifWe(sir, 'bootstrap a domain metaspace and create the directory', async () => {
49
- const keystoneAddr = 'keystone_alice^abcde1234567890';
50
- const metaspace = await bootstrapDomainMetaspace(keystoneAddr, testDataDir);
51
-
52
- iReckon(sir, metaspace).isGonnaBe(metaspace);
53
- iReckon(sir, metaspace.initialized).isGonnaBe(true);
54
-
55
- const expectedPath = getDomainRootPath('abcde1234567890', testDataDir);
56
- iReckon(sir, existsSync(expectedPath)).isGonnaBe(true);
57
-
58
- // The ibgib framework uses a specific subdirectory structure for the bootstrap
59
- const bootstrapFile = join(expectedPath, 'ibgib', '000_zerospace', 'gib', 'bootstrap.json');
60
- iReckon(sir, existsSync(bootstrapFile)).isGonnaBe(true);
61
-
62
- // Also check that the default space was created
63
- const defaultSpaceDir = join(expectedPath, 'ibgib', 'default');
64
- iReckon(sir, existsSync(defaultSpaceDir)).isGonnaBe(true);
65
- });
66
-
67
- await ifWe(sir, 'integrate with KeystoneService and bootstrap 5 unique domains', async () => {
68
- const keystoneService = new KeystoneService_V1();
69
- const mockMetaspace = new MockMetaspace() as any;
70
- const mockSpace = new MockSpace() as any;
71
- const config = createStandardPoolConfig({ id: POOL_ID_DEFAULT, salt: POOL_ID_DEFAULT });
72
-
73
- const identities = ['Alice', 'Bob', 'Charlie', 'Dave', 'Eve'];
74
-
75
- for (const name of identities) {
76
- // 1. Generate an actual genesis keystone for this identity
77
- const keystone = await keystoneService.genesis({
78
- masterSecret: `SecretFor_${name}`,
79
- configs: [config],
80
- metaspace: mockMetaspace,
81
- space: mockSpace,
82
- });
83
- const addr = getIbGibAddr({ ibGib: keystone });
84
- const tjpGib = keystone.gib!;
85
-
86
- if (logalot) { console.log(`[test] Bootstrapping domain for ${name}: ${addr} (I: 7ba45828f2719ca578a1f078ff16e126)`); }
87
-
88
- // 2. Drive the bootstrap logic
89
- const metaspace = await bootstrapDomainMetaspace(addr, testDataDir);
90
- iReckon(sir, metaspace.initialized).isGonnaBe(true);
91
-
92
- // 3. Verify the unique path
93
- const expectedPath = getDomainRootPath(tjpGib, testDataDir);
94
- iReckon(sir, existsSync(expectedPath)).isGonnaBe(true);
95
-
96
- const bootstrapFile = join(expectedPath, 'ibgib', '000_zerospace', 'gib', 'bootstrap.json');
97
- iReckon(sir, existsSync(bootstrapFile)).isGonnaBe(true);
98
- }
99
- });
100
- });
@@ -1,85 +0,0 @@
1
- /**
2
- * @module node-metaspace driven by a node+fs storage substrate
3
- *
4
- * a metaspace is a space of spaces. it adds/removes spaces and does
5
- * inter-spatial composition related things. it's similar to what people think
6
- * of in terms of a node in a p2p network.
7
- */
8
-
9
- import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
10
- import { IbGibSpaceAny } from '@ibgib/core-gib/dist/witness/space/space-base-v1.mjs';
11
- import { IbGibCacheService } from '@ibgib/core-gib/dist/common/cache/cache-types.mjs';
12
- import { MetaspaceBase } from '@ibgib/core-gib/dist/witness/space/metaspace/metaspace-base.mjs';
13
- import { MetaspaceFactory } from '@ibgib/core-gib/dist/witness/space/metaspace/metaspace-types.mjs';
14
-
15
- import { GLOBAL_LOG_A_LOT, GLOBAL_TIMER_NAME } from '../server-constants.mjs';
16
-
17
- const logalot = GLOBAL_LOG_A_LOT;
18
-
19
- /**
20
- *
21
- */
22
- export class Metaspace_Nodeindexedspace extends MetaspaceBase {
23
-
24
- // we won't get an object back, only a DTO ibGib essentially
25
- protected lc: string = `[${Metaspace_Nodeindexedspace.name}]`;
26
-
27
- get zeroSpace(): IbGibSpaceAny {
28
- const lc = `[${this.lc}][get zeroSpace]`;
29
- if (this.metaspaceFactory.fnZeroSpaceFactory) {
30
- return this.metaspaceFactory.fnZeroSpaceFactory();
31
- } else {
32
- throw new Error(`${lc} (UNEXPECTED) this.metaspaceFactory.fnZeroSpaceFactory falsy. not initialized? (E: 9e6c6954dacd868b18c9f34a71e63523)`);
33
- }
34
- }
35
-
36
- constructor(
37
- // public modalController: ModalController,
38
- // public alertController: AlertController,
39
- protected cacheSvc: IbGibCacheService | undefined,
40
- // private latestCacheSvc: IonicStorageLatestIbgibCacheService,
41
- ) {
42
- super(cacheSvc);
43
- const lc = `${this.lc}[ctor]`;
44
- if (logalot) {
45
- console.log(`${lc}${GLOBAL_TIMER_NAME}`);
46
- console.timeLog(GLOBAL_TIMER_NAME);
47
- console.log(`${lc} created. (I: 5690dc2ebf774df3a442bd463dee7455)`);
48
- }
49
-
50
- }
51
-
52
- protected async initializeMetaspaceFactory({ metaspaceFactory }: {
53
- metaspaceFactory: MetaspaceFactory,
54
- }): Promise<void> {
55
- const lc = `[${this.initializeMetaspaceFactory.name}]`;
56
- try {
57
- if (logalot) { console.log(`${lc} starting... (I: 5a29670439ac47634dcdfe25225c8223)`); }
58
- if (!metaspaceFactory) { throw new Error(`(UNEXPECTED) metaspaceFactory falsy? space-gib server initialization should have guaranteed this to be truthy (E: f305356c2978cf0ee8062fd818940826)`); }
59
- if (!metaspaceFactory.fnDefaultLocalSpaceFactory) { throw new Error(`(UNEXPECTED) !metaspaceFactory.fnDefaultLocalSpaceFactory? space-gib server initialization should have guaranteed this to be truthy (E: 884281e5251bd65528eb7b3f48737826)`); }
60
- if (!metaspaceFactory.fnDtoToSpace) { throw new Error(`(UNEXPECTED) !metaspaceFactory.fnDtoToSpace? space-gib server initialization should have guaranteed this to be truthy (E: 885b7828606f36f9183bb9e687e25826)`); }
61
- if (!metaspaceFactory.fnZeroSpaceFactory) { throw new Error(`(UNEXPECTED) !metaspaceFactory.fnZeroSpaceFactory? space-gib server initialization should have guaranteed this to be truthy (E: 336cf6655758bad4aaeea8ce08d8c826)`); }
62
-
63
- await super.initializeMetaspaceFactory({ metaspaceFactory });
64
- } catch (error) {
65
- console.error(`${lc} ${extractErrorMsg(error)}`);
66
- throw error;
67
- } finally {
68
- if (logalot) { console.log(`${lc} complete.`); }
69
- }
70
- }
71
-
72
- // async initializeZeroSpace(): Promise<void> {
73
- // const lc = `[${this.initializeZeroSpace.name}]`;
74
- // try {
75
- // if (logalot) { console.log(`${lc} starting... (I: a3364e9601c70c8163e67868e5d67723)`); }
76
-
77
- // } catch (error) {
78
- // console.error(`${lc} ${extractErrorMsg(error)}`);
79
- // throw error;
80
- // } finally {
81
- // if (logalot) { console.log(`${lc} complete.`); }
82
- // }
83
- // }
84
-
85
- }
@@ -1,101 +0,0 @@
1
- /** Maximum allowed path length to prevent DoS. */
2
- export const MAX_PATH_LENGTH = 2048;
3
-
4
- /**
5
- * Blacklisted patterns for any incoming path.
6
- * We iterate through these for a quick security check.
7
- */
8
- export const FORBIDDEN_PATTERNS = [
9
- /\.\./, // No directory traversal
10
- /\/\//, // No redundant slashes
11
- /\0/, // No null bytes
12
- /(^|\/)\./, // No hidden/dot-files (e.g. .env, .git)
13
- ];
14
-
15
- /**
16
- * Regex for valid API paths.
17
- *
18
- * IMPORTANT: See IMPLEMENTATION.md for overall path schema documentation.
19
- *
20
- * Paths are URL encoded. The `ib` and `gib` portions of addresses are captured explicitly
21
- * by splitting on `%5E` or `^`.
22
- */
23
- export const API_PATH_REGEXES = {
24
- /**
25
- * /api/health
26
- */
27
- HEALTH: /^\/api\/health\/?$/,
28
-
29
- /**
30
- * /api/ibgib/graph/:domainAddr/:ibgibAddr
31
- */
32
- IBGIB_GRAPH: /^\/api\/ibgib\/graph\/([^\/]+?)(?:%5E|\^)([^\/]+)\/([^\/]+?)(?:%5E|\^)([^\/]+)\/?$/,
33
-
34
- /**
35
- * /api/ibgib/:domainAddr/:ibgibAddr
36
- */
37
- IBGIB_ADDR: /^\/api\/ibgib\/([^\/]+?)(?:%5E|\^)([^\/]+)\/([^\/]+?)(?:%5E|\^)([^\/]+)\/?$/,
38
-
39
- /**
40
- * /api/keystone
41
- */
42
- KEYSTONE: /^\/api\/keystone\/?$/,
43
-
44
- /**
45
- * /api/keystone/:domainAddr
46
- */
47
- KEYSTONE_ADDR: /^\/api\/keystone\/([^\/]+?)(?:%5E|\^)([^\/]+)\/?$/,
48
-
49
- /**
50
- * /api/ibgib
51
- */
52
- IBGIB_BASE: /^\/api\/ibgib\/?$/,
53
-
54
- /**
55
- * /api/keystone/evolve/:domainAddr
56
- */
57
- KEYSTONE_EVOLVE: /^\/api\/keystone\/evolve\/([^\/]+?)(?:%5E|\^)([^\/]+)\/?$/,
58
-
59
- /**
60
- * /api/keystone/genesis
61
- *
62
- * Accepts a single genesis-frame keystone ibgib and creates a new
63
- * domain-isolated metaspace for it. No `:domainAddr` in the URL because
64
- * the server derives the domain from the incoming keystone ibgib itself.
65
- *
66
- * Parallel to /api/keystone/evolve.
67
- */
68
- KEYSTONE_GENESIS: /^\/api\/keystone\/genesis\/?$/,
69
-
70
- /**
71
- * /api/debug/ws-echo
72
- *
73
- * Minimal WebSocket echo endpoint for validating WS plumbing (Traefik
74
- * passthrough, server upgrade handling, client connectivity) before
75
- * integrating real sync logic.
76
- *
77
- * ## intent
78
- *
79
- * NOT for production use. Remove or gate behind a flag once WS sync is
80
- * confirmed working end-to-end.
81
- */
82
- DEBUG_WS_ECHO: /^\/api\/debug\/ws-echo\/?$/,
83
- /**
84
- * /api/sync/ws/:domainAddr
85
- */
86
- SYNC_WS: /^\/api\/sync\/ws\/([^\/]+?)(?:%5E|\^)([^\/]+)\/?$/,
87
- /**
88
- * /api/identity/sso/link
89
- */
90
- IDENTITY_SSO_LINK: /^\/api\/identity\/sso\/link\/?$/,
91
- /**
92
- * /api/identity/sso/login
93
- */
94
- IDENTITY_SSO_LOGIN: /^\/api\/identity\/sso\/login\/?$/,
95
- /**
96
- * /api/identity/sso/config
97
- */
98
- IDENTITY_SSO_CONFIG: /^\/api\/identity\/sso\/config\/?$/,
99
- };
100
-
101
- export const VALID_STATIC_PATH_REGEX = /^\/[a-zA-Z0-9\-_\.\/]*$/;
@@ -1,101 +0,0 @@
1
- import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
2
- import { validateIbGibAddr } from '@ibgib/ts-gib/dist/V1/validate-helper.mjs';
3
-
4
- import { GLOBAL_LOG_A_LOT } from './server-constants.mjs';
5
- import { FORBIDDEN_PATTERNS, MAX_PATH_LENGTH, VALID_STATIC_PATH_REGEX } from './path-constants.mjs';
6
-
7
- const logalot = GLOBAL_LOG_A_LOT;
8
-
9
- /**
10
- * little helper function just to DRY out code.
11
- */
12
- export function runtimeErrorValidationStrings(lc: string, error: any): string[] {
13
- const emsg = `${lc} ${extractErrorMsg(error)}`;
14
- console.error(emsg);
15
- return [`runtime error: ${emsg}`];
16
- }
17
-
18
- /**
19
- * Validates that a path is safe from common attacks and under max length.
20
- */
21
- export function validateRawPath(rawPath: string): string[] {
22
- const lc = `[${validateRawPath.name}]`;
23
- try {
24
- if (logalot) { console.log(`${lc} starting... (I: 80bd6f63e6c83b8e943b90383e044826)`); }
25
-
26
- const errors: string[] = [];
27
-
28
- if (!rawPath || rawPath.length > MAX_PATH_LENGTH) {
29
- errors.push(`path too long. MAX_PATH_LENGTH: ${MAX_PATH_LENGTH}, incoming path length: ${rawPath.length}`);
30
- }
31
-
32
- for (const pattern of FORBIDDEN_PATTERNS) {
33
- if (pattern.test(rawPath)) {
34
- errors.push(`invalid path. contains forbidden pattern.source: ${pattern.source} (E: 56a228fb9473a086b83d0088b5af1826)`);
35
- }
36
- }
37
-
38
- return errors;
39
- } catch (error) {
40
- return runtimeErrorValidationStrings(lc, error);
41
- } finally {
42
- if (logalot) { console.log(`${lc} complete.`); }
43
- }
44
- }
45
-
46
- /**
47
- * simple boolean wrapper for {@link validateRawPath}
48
- */
49
- export function isSafePath(path: string): boolean {
50
- const errors = validateRawPath(path);
51
- return errors.length === 0;
52
- }
53
-
54
- export function validateStaticPath(path: string): string[] {
55
- const lc = `[${validateStaticPath.name}]`;
56
- try {
57
- if (logalot) { console.log(`${lc} starting... (I: 4f9cf8ffd528c7dd88324529528eeb26)`); }
58
-
59
- const errors: string[] = [];
60
-
61
- if (!VALID_STATIC_PATH_REGEX.test(path)) {
62
- errors.push(`static path fails to match VALID_STATIC_PATH_REGEX of ${VALID_STATIC_PATH_REGEX} (E: dfdf38a0439d4d09186e6fa81ab66826) `);
63
- }
64
-
65
- return errors;
66
- } catch (error) {
67
- return runtimeErrorValidationStrings(lc, error);
68
- } finally {
69
- if (logalot) { console.log(`${lc} complete.`); }
70
- }
71
- }
72
-
73
- /**
74
- * Validates a static file path.
75
- */
76
- export function isValidStaticPath(path: string): boolean {
77
- if (!isSafePath(path)) { return false; }
78
- const errors = validateStaticPath(path);
79
- return errors.length === 0;
80
- }
81
-
82
- /**
83
- * Decodes and validates a single ibGib address segment from a URL path.
84
- * Uses the library's internal validateIbGibAddr for deep checking.
85
- */
86
- export function validateAddressSegment(encodedAddr: string): string[] {
87
- const lc = `[${validateAddressSegment.name}]`;
88
- try {
89
- if (logalot) { console.log(`${lc} starting... (I: 1bb9ff7e1aefb77b670b2519658cc126)`); }
90
- if (!encodedAddr) {
91
- throw new Error(`(UNEXPECTED) falsy encodedAddr? We're expecting to have already guaranteed there are no double-slashes (i.e. no empty segments) (E: 7be7668e7b8c9fbbdf56f0fe19f92e26)`);
92
- }
93
- const addr = decodeURIComponent(encodedAddr);
94
- const errors = validateIbGibAddr({ addr });
95
- return errors ?? [];
96
- } catch (error) {
97
- return runtimeErrorValidationStrings(lc, error);
98
- } finally {
99
- if (logalot) { console.log(`${lc} complete.`); }
100
- }
101
- }
@@ -1,99 +0,0 @@
1
- // import { respecfully, iReckon, ifWe } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
2
- // import {
3
- // isSafePath, isValidStaticPath,
4
- // // API_PATHS, getPathParams, MAX_PATH_LENGTH,
5
- // validateAddressSegment
6
- // } from './path-helper.mjs';
7
- // import { MAX_PATH_LENGTH } from './path-constants.mts';
8
-
9
- // const sir = `[${import.meta.url}]`;
10
-
11
- // await respecfully(sir, 'Path Helper Validation', async () => {
12
-
13
- // await respecfully(sir, 'isSafePath', async () => {
14
- // await ifWe(sir, 'valid simple paths', async () => {
15
- // iReckon(sir, isSafePath('/api/health')).isGonnaBeTrue();
16
- // iReckon(sir, isSafePath('/index.html')).isGonnaBeTrue();
17
- // });
18
-
19
- // await ifWe(sir, 'too long paths', async () => {
20
- // const longPath = '/'.repeat(MAX_PATH_LENGTH + 1);
21
- // iReckon(sir, isSafePath(longPath)).isGonnaBeFalse();
22
- // });
23
-
24
- // await ifWe(sir, 'directory traversal attempts', async () => {
25
- // iReckon(sir, isSafePath('/../etc/passwd')).isGonnaBeFalse();
26
- // iReckon(sir, isSafePath('/api/../../etc/passwd')).isGonnaBeFalse();
27
- // });
28
-
29
- // await ifWe(sir, 'null bytes', async () => {
30
- // iReckon(sir, isSafePath('/api/health\0')).isGonnaBeFalse();
31
- // });
32
-
33
- // await ifWe(sir, 'redundant slashes', async () => {
34
- // iReckon(sir, isSafePath('/api//health')).isGonnaBeFalse();
35
- // });
36
-
37
- // await ifWe(sir, 'dot-file access', async () => {
38
- // iReckon(sir, isSafePath('/.env')).isGonnaBeFalse();
39
- // iReckon(sir, isSafePath('/scripts/.hidden')).isGonnaBeFalse();
40
- // iReckon(sir, isSafePath('/.git/config')).isGonnaBeFalse();
41
- // });
42
- // });
43
-
44
- // await respecfully(sir, 'validateAddressSegment', async () => {
45
- // await ifWe(sir, 'valid encoded addresses', async () => {
46
- // const validGib = 'a'.repeat(64);
47
- // iReckon(sir, validateAddressSegment(`someib%5E${validGib}`)).isGonnaBeTrue();
48
- // // gib with dots (common in sha256.sha256)
49
- // const dotGib = `${validGib}.${validGib}`;
50
- // iReckon(sir, validateAddressSegment(`someib%5E${dotGib}`)).isGonnaBeTrue();
51
- // });
52
-
53
- // await ifWe(sir, 'invalid encoded addresses', async () => {
54
- // iReckon(sir, validateAddressSegment('no_delim')).isGonnaBeFalse();
55
- // iReckon(sir, validateAddressSegment('%5Estarts_with_delim')).isGonnaBeFalse();
56
- // iReckon(sir, validateAddressSegment('too%5Emany%5Edelims')).isGonnaBeFalse();
57
- // iReckon(sir, validateAddressSegment('too%5Emany..dots')).isGonnaBeFalse();
58
- // iReckon(sir, validateAddressSegment('invalid_chars_!%5Eabc')).isGonnaBeFalse();
59
- // });
60
- // });
61
-
62
- // await respecfully(sir, 'API_PATHS Regexes', async () => {
63
- // await ifWe(sir, 'HEALTH regex', async () => {
64
- // iReckon(sir, API_PATHS.HEALTH.test('/api/health')).isGonnaBeTrue();
65
- // iReckon(sir, API_PATHS.HEALTH.test('/api/health/')).isGonnaBeTrue();
66
- // iReckon(sir, API_PATHS.HEALTH.test('/api/health/more')).isGonnaBeFalse();
67
- // });
68
-
69
- // await ifWe(sir, 'IBGIB_ADDR regex', async () => {
70
- // // valid encoded addr: someib%5Esomegib
71
- // const addr = '/api/ibgib/someib%5Esomegib';
72
- // iReckon(sir, API_PATHS.IBGIB_ADDR.test(addr)).isGonnaBeTrue();
73
-
74
- // const params = getPathParams(addr, API_PATHS.IBGIB_ADDR);
75
- // iReckon(sir, params?.[0]).isGonnaBe('someib%5Esomegib');
76
- // });
77
-
78
- // await ifWe(sir, 'IBGIB_GRAPH regex', async () => {
79
- // const path = '/api/ibgib/graph/someib%5Esomegib';
80
- // iReckon(sir, API_PATHS.IBGIB_GRAPH.test(path)).isGonnaBeTrue();
81
-
82
- // const params = getPathParams(path, API_PATHS.IBGIB_GRAPH);
83
- // iReckon(sir, params?.[0]).isGonnaBe('someib%5Esomegib');
84
- // });
85
-
86
- // await ifWe(sir, 'KEYSTONE regex', async () => {
87
- // iReckon(sir, API_PATHS.KEYSTONE.test('/api/keystone')).isGonnaBeTrue();
88
- // iReckon(sir, API_PATHS.KEYSTONE.test('/api/keystone/')).isGonnaBeTrue();
89
- // });
90
-
91
- // await ifWe(sir, 'KEYSTONE_EVOLVE regex', async () => {
92
- // const path = '/api/keystone/evolve/someib%5Esomegib';
93
- // iReckon(sir, API_PATHS.KEYSTONE_EVOLVE.test(path)).isGonnaBeTrue();
94
-
95
- // const params = getPathParams(path, API_PATHS.KEYSTONE_EVOLVE);
96
- // iReckon(sir, params?.[0]).isGonnaBe('someib%5Esomegib');
97
- // });
98
- // });
99
- // });
@@ -1,29 +0,0 @@
1
- # serve-gib changelog
2
-
3
- _note: as you implement features/fixes/etc., please document them here under the "Working Version" section._
4
-
5
- ## Working Version
6
-
7
- * impl: initial core infrastructure
8
- * defined `RouteInfo`, `ResponseResult`, and `ServeGibHandler` foundational types.
9
- * implemented `ServeGib_V1` with pipeline-based request processing.
10
- * added robust `try..catch..finally` pattern with built-in access logging and timing.
11
- * impl: handler "pit of success" plumbing
12
- * implemented `ServeGibHandlerBase` abstract class to reduce boilerplate.
13
- * automated route matching via `canHandleRoute` and `regex` properties.
14
- * implemented `handleRouteImpl` pattern to separate plumbing from business logic.
15
- * added generic `TQueryParams` support with automated parsing via `parseQueryParams`.
16
- * feat: hierarchical handler organization
17
- * established folder-based grouping mirroring URL paths (e.g., `handlers/api/ibgib/`).
18
- * enforced "one handler per hard route" and "one handler per file" architectural guidelines.
19
- * feat: concrete core handlers
20
- * `HealthHandler`: Simple liveness probe.
21
- * `IbGibHandler`: Single ibGib address resolution.
22
- * `IbGibGraphHandler`: Complex graph/BFS retrieval.
23
- * `KeystoneHandler`: Genesis and evolution turn persistence.
24
- * `StaticFileHandler`: High-performance asset serving with SPA history fallback.
25
- * `ErrorHandler`: Standardized 404 and fatal error reporting.
26
- * docs: architectural guidance
27
- * created `README.md` with detailed guidance on handler philosophy and plumbing usage.
28
- * test: validation suite
29
- * implemented `serve-gib.respec.mts` verifying orchestrator pipeline and `RouteInfo` construction.
@@ -1,43 +0,0 @@
1
- # serve-gib Microframework
2
-
3
- A minimalist, high-observability HTTP microframework for ibgib servers.
4
-
5
- ## Architecture & Guidance
6
-
7
- ### 1. Handler Philosophy
8
- * **One handler per "hard" route**: A handler should correspond to a unique URL path pattern, disregarding query parameters.
9
- * **One regex per handler**: Concrete handlers should ideally define a single `regex` property for routing.
10
- * **One handler per file**: Each route handler should live in its own file within the `handlers/` directory (e.g., `handlers/api/health.handler.mts`).
11
- * The subfolders should match the hard route of the handler, e.g., `api/ibgib` route should be in the `handlers/api/ibgib` subfolder.
12
-
13
- ### 2. The "Pit of Success" Plumbing
14
- The `ServeGibHandlerBase` class provides built-in plumbing to reduce boilerplate:
15
- * **`handleRoute` (Plumbing)**: Automatically checks `canHandleRoute`, parses query parameters, and provides a top-level `try..catch` block for logging and error reporting.
16
- * **`handleRouteImpl` (Implementation)**: This is where the core logic of the handler resides.
17
- * **`canHandleRoute`**: Defaults to a regex match on `info.pathname`. Override this for more complex routing logic.
18
-
19
- ### 3. Query Parameters
20
- Query parameters are handled via the `parseQueryParams` method in the base class.
21
- * By default, it parses URL search params into a key-value object.
22
- * Handlers can override this method to perform custom parsing or validation (e.g., parsing JSON strings from query params).
23
- * The base class is generic: `ServeGibHandlerBase<TQueryParams = any>`.
24
-
25
- ### 4. RouteInfo
26
- The `RouteInfo` object provides an enriched view of the incoming request:
27
- * `decodedPathSegments`: Pre-split and decoded path parts.
28
- * `protocol`, `method`, `body`: Basic HTTP info.
29
- * `queryParams`: The result of the handler's `parseQueryParams` call.
30
-
31
- ## Pipeline Order
32
- The `ServeGib_V1` executes handlers in the order they are provided in the `handlers` array. The first handler to return a non-undefined `ResponseResult` wins.
33
- * **Tip**: Order specific routes (like `/api/ibgib/graph`) before more general ones (like `/api/ibgib`).
34
- * **Fallback**: The `ErrorHandler` is used as a final catch-all for 404s and fatal errors.
35
-
36
- ## Docker / Server Resilience Note
37
- When deploying or running this service (or any downstream applications) within a Docker Compose environment, ensure that the `docker-compose.yml` configuration specifies a restart policy such as:
38
- ```yaml
39
- services:
40
- space-gib:
41
- restart: unless-stopped
42
- ```
43
- This ensures that the container auto-recovers and restarts if the node process crashes due to unhandled events (such as raw TCP socket `ECONNRESET` exceptions from client refreshes).
@@ -1 +0,0 @@
1
- export const GLOBAL_LOG_A_LOT = false;
@@ -1,105 +0,0 @@
1
- /**
2
- * @module handlers/api/debug/ws-echo.handler
3
- *
4
- * Minimal WebSocket echo handler for validating end-to-end WebSocket
5
- * plumbing: Traefik passthrough → Node.js upgrade event → browser client.
6
- *
7
- * ## intent
8
- *
9
- * This is a diagnostic-only endpoint. The goal is to confirm that:
10
- * 1. Traefik correctly forwards WS upgrade requests to the container
11
- * 2. The Node.js `upgrade` event fires and we can accept the connect handshake
12
- * 3. The browser client can open a wss:// connection and exchange frames
13
- *
14
- * Once WS sync is confirmed end-to-end, this handler can be removed or
15
- * gated behind an env flag.
16
- *
17
- * ## endpoint
18
- *
19
- * `GET /api/debug/ws-echo` (with Upgrade: websocket header)
20
- *
21
- * On connect: server sends `{"status":"connected","msg":"ws-echo ready"}`
22
- * On message: server echoes `{"echo":<original message>}`
23
- * On close: server logs and cleans up
24
- */
25
-
26
- import { IncomingMessage } from 'node:http';
27
- import { Socket } from 'node:net';
28
-
29
- import { extractErrorMsg } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
30
-
31
- import { GLOBAL_LOG_A_LOT } from '../../../constants.mjs';
32
- import {
33
- encodeTextFrame, decodeTextFrame, encodeCloseFrame, performConnect,
34
- WebSocketFrameDecoder
35
- } from '../../ws/ws-helper.mjs';
36
- import { API_PATH_REGEXES } from '../../../../path-constants.mjs';
37
-
38
- const logalot = GLOBAL_LOG_A_LOT;
39
-
40
- const lc = `[WsEchoHandler]`;
41
-
42
-
43
- // ---------------------------------------------------------------------------
44
- // Public handler
45
- // ---------------------------------------------------------------------------
46
-
47
- /**
48
- * Returns true if this request is a WebSocket upgrade to the debug echo path.
49
- * Call this from the server's `upgrade` event listener.
50
- */
51
- export function canHandleWsUpgrade(req: IncomingMessage): boolean {
52
- const pathname = new URL(req.url ?? '/', 'http://localhost').pathname;
53
- return API_PATH_REGEXES.DEBUG_WS_ECHO.test(pathname);
54
- }
55
-
56
- /**
57
- * Accepts the WebSocket upgrade and sets up the echo loop.
58
- *
59
- * Client will receive a JSON `{status:"connected"}` on open, and
60
- * `{echo:<msg>}` for every message it sends.
61
- */
62
- export function handleWsEchoUpgrade(req: IncomingMessage, socket: Socket, head: Buffer): void {
63
- const lc_fn = `${lc}[handleWsEchoUpgrade]`;
64
- try {
65
- if (logalot) { console.log(`${lc_fn} starting...`); }
66
-
67
- const ok = performConnect(req, socket);
68
- if (!ok) { return; }
69
-
70
- // Send the "connected" greeting
71
- socket.write(encodeTextFrame(JSON.stringify({ status: 'connected', msg: 'ws-echo ready' })));
72
-
73
- // Echo loop
74
- const decoder = new WebSocketFrameDecoder();
75
- socket.on('data', (data: Buffer) => {
76
- try {
77
- decoder.addChunk(data);
78
- let frameText = decoder.nextFrame();
79
- while (frameText !== null) {
80
- if (logalot) { console.log(`${lc_fn} echo: ${frameText}`); }
81
- socket.write(encodeTextFrame(JSON.stringify({ echo: frameText })));
82
- frameText = decoder.nextFrame();
83
- }
84
- } catch (error) {
85
- console.error(`${lc_fn} error in data handler: ${extractErrorMsg(error)}`);
86
- socket.write(encodeCloseFrame(1000));
87
- socket.end();
88
- }
89
- });
90
-
91
- socket.on('error', (error) => {
92
- console.error(`${lc_fn} socket error: ${extractErrorMsg(error)}`);
93
- });
94
-
95
- socket.on('close', () => {
96
- if (logalot) { console.log(`${lc_fn} socket closed.`); }
97
- });
98
-
99
- } catch (error) {
100
- console.error(`${lc_fn} ${extractErrorMsg(error)}`);
101
- socket.destroy();
102
- } finally {
103
- if (logalot) { console.log(`${lc_fn} complete.`); }
104
- }
105
- }