feat: export messaging-ui public API and enforce transport boundary
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
|||||||
|
// @vitest-environment node
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { join } from 'node:path';
|
||||||
|
|
||||||
|
// This package is ESM ("type": "module") — __dirname does not exist here.
|
||||||
|
const SRC = fileURLToPath(new URL('.', import.meta.url));
|
||||||
|
const ADAPTERS = join(SRC, 'adapters');
|
||||||
|
|
||||||
|
function tsFilesIn(dir: string): string[] {
|
||||||
|
const out: string[] = [];
|
||||||
|
for (const entry of readdirSync(dir)) {
|
||||||
|
const full = join(dir, entry);
|
||||||
|
if (statSync(full).isDirectory()) out.push(...tsFilesIn(full));
|
||||||
|
else if (/\.tsx?$/.test(full)) out.push(full);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The whole premise of this package: core renders, adapters transport. If core ever
|
||||||
|
// imports a transport, an app on a different backend pays for socket code it cannot
|
||||||
|
// use — which is exactly how iios-message-web welded itself to MessageSocket.
|
||||||
|
describe('transport boundary', () => {
|
||||||
|
const coreFiles = tsFilesIn(SRC).filter((f) => !f.startsWith(ADAPTERS) && !/\.test\.tsx?$/.test(f));
|
||||||
|
|
||||||
|
it('has core files to check', () => {
|
||||||
|
expect(coreFiles.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('core never imports a transport package', () => {
|
||||||
|
const offenders: string[] = [];
|
||||||
|
for (const file of coreFiles) {
|
||||||
|
const content = readFileSync(file, 'utf8');
|
||||||
|
if (/@insignia\/iios-kernel-client|socket\.io|@abe-kap\/appshell-sdk/.test(content)) {
|
||||||
|
offenders.push(file.replace(SRC, 'src'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect(offenders).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1 +1,24 @@
|
|||||||
export {};
|
// Public API. Components land in Plan 2; adapters/kernel in Plan 3.
|
||||||
|
//
|
||||||
|
// `runAdapterConformance` is deliberately NOT exported here — it imports vitest and
|
||||||
|
// ships from the './conformance' subpath so consumers never pull a test runner into
|
||||||
|
// their production bundle.
|
||||||
|
export { MessagingProvider, useAdapter } from './provider';
|
||||||
|
export { useConversations } from './hooks/use-conversations';
|
||||||
|
export { useMessages } from './hooks/use-messages';
|
||||||
|
export { isOwnMessage } from './types';
|
||||||
|
|
||||||
|
export type { MessagingAdapter } from './adapter';
|
||||||
|
export type { ConversationsState } from './hooks/use-conversations';
|
||||||
|
export type { MessagesState, UiMessage } from './hooks/use-messages';
|
||||||
|
export type {
|
||||||
|
Attachment,
|
||||||
|
Conversation,
|
||||||
|
Membership,
|
||||||
|
Message,
|
||||||
|
MessageEvent,
|
||||||
|
Person,
|
||||||
|
Reaction,
|
||||||
|
SendOpts,
|
||||||
|
Unsubscribe,
|
||||||
|
} from './types';
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default defineConfig({
|
|||||||
// entry, never reachable from `index`, because it imports vitest, and bundling
|
// entry, never reachable from `index`, because it imports vitest, and bundling
|
||||||
// that into the main barrel would drag a test runner into every consumer's
|
// that into the main barrel would drag a test runner into every consumer's
|
||||||
// production build.
|
// production build.
|
||||||
entry: ['src/index.ts'],
|
entry: ['src/index.ts', 'src/adapters/mock.ts', 'src/conformance.ts'],
|
||||||
format: ['esm'],
|
format: ['esm'],
|
||||||
dts: true,
|
dts: true,
|
||||||
clean: true,
|
clean: true,
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ const ALLOWED = {
|
|||||||
'@insignia/iios-adapter-sdk': ['@insignia/iios-contracts'],
|
'@insignia/iios-adapter-sdk': ['@insignia/iios-contracts'],
|
||||||
'@insignia/iios-service': ['@insignia/iios-contracts', '@insignia/iios-testkit', '@insignia/iios-adapter-sdk'],
|
'@insignia/iios-service': ['@insignia/iios-contracts', '@insignia/iios-testkit', '@insignia/iios-adapter-sdk'],
|
||||||
'@insignia/iios-message-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
'@insignia/iios-message-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
||||||
|
// Core is transport-free; only ./adapters/kernel may import the kernel client.
|
||||||
|
// This map is package-level and cannot express that subpath rule — the finer
|
||||||
|
// constraint is enforced by packages/iios-messaging-ui/src/boundary.test.ts.
|
||||||
|
'@insignia/iios-messaging-ui': ['@insignia/iios-kernel-client'],
|
||||||
'@insignia/iios-inbox-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
'@insignia/iios-inbox-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
||||||
'@insignia/iios-community-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
'@insignia/iios-community-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
||||||
'@insignia/iios-ai-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
'@insignia/iios-ai-web': ['@insignia/iios-contracts', '@insignia/iios-kernel-client'],
|
||||||
|
|||||||
Reference in New Issue
Block a user