@stardeck-customer-apps/testing 0.3.0 → 0.3.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/dist/index.js CHANGED
@@ -99,6 +99,7 @@ var state = globalSingleton("state", () => ({
99
99
  emailCounter: 0,
100
100
  identities: /* @__PURE__ */ new Map(),
101
101
  identityLinks: [],
102
+ identityMemory: [],
102
103
  allowNetwork: false
103
104
  }));
104
105
  function requireDb() {
@@ -756,16 +757,108 @@ async function handleAttachLink(identityId, request) {
756
757
  state.identityLinks.push(link);
757
758
  return success({ link });
758
759
  }
760
+ function memoryDto(m) {
761
+ return {
762
+ id: m.id,
763
+ source: m.source,
764
+ kind: m.kind,
765
+ content: m.content,
766
+ metadata: m.metadata,
767
+ createdAt: m.createdAt
768
+ };
769
+ }
770
+ async function handleResolve(request) {
771
+ const body = await readBody(request);
772
+ const type = body.type;
773
+ if (type !== "person" && type !== "account") {
774
+ return failure("type must be 'person' or 'account'");
775
+ }
776
+ if (type === "account") {
777
+ return failure(
778
+ "resolveOrCreate is for channel-linked persons; an account is not resolvable by link \u2014 create it explicitly instead.",
779
+ 409
780
+ );
781
+ }
782
+ const link = body.link;
783
+ const kind = link?.kind;
784
+ const externalId = link?.externalId;
785
+ if (typeof kind !== "string" || !LINK_KINDS.has(kind)) {
786
+ return failure(`link.kind must be one of: ${[...LINK_KINDS].join(", ")}`);
787
+ }
788
+ if (typeof externalId !== "string" || !externalId) {
789
+ return failure("link.externalId is required");
790
+ }
791
+ const existingLink = state.identityLinks.find(
792
+ (l) => l.kind === kind && l.externalId === externalId
793
+ );
794
+ if (existingLink) {
795
+ const identity2 = state.identities.get(existingLink.identityId);
796
+ if (identity2) return success({ identity: identity2, created: false });
797
+ }
798
+ const identity = {
799
+ id: import_node_crypto3.default.randomUUID(),
800
+ type,
801
+ parentId: null,
802
+ displayName: body.displayName ?? null,
803
+ profile: body.profile ?? {},
804
+ status: "active",
805
+ mergedIntoId: null,
806
+ externalRef: null,
807
+ createdAt: now(),
808
+ updatedAt: now()
809
+ };
810
+ state.identities.set(identity.id, identity);
811
+ state.identityLinks.push({
812
+ id: import_node_crypto3.default.randomUUID(),
813
+ identityId: identity.id,
814
+ kind,
815
+ externalId,
816
+ verified: true,
817
+ createdAt: now()
818
+ });
819
+ return success({ identity, created: true });
820
+ }
821
+ async function handleWriteMemory(identityId, request) {
822
+ if (!state.identities.get(identityId)) return failure("Identity not found", 404);
823
+ const body = await readBody(request);
824
+ if (typeof body.content !== "string" || !body.content) return failure("content is required");
825
+ const entry = {
826
+ id: import_node_crypto3.default.randomUUID(),
827
+ identityId,
828
+ source: typeof body.source === "string" ? body.source : "app",
829
+ kind: typeof body.kind === "string" ? body.kind : "fact",
830
+ content: body.content,
831
+ metadata: body.metadata ?? {},
832
+ createdAt: now()
833
+ };
834
+ state.identityMemory.push(entry);
835
+ return success({ memory: memoryDto(entry) });
836
+ }
837
+ function handleListMemory(identityId, request) {
838
+ if (!state.identities.get(identityId)) return failure("Identity not found", 404);
839
+ let rows = state.identityMemory.filter((m) => m.identityId === identityId).reverse();
840
+ const limitParam = new URL(request.url).searchParams.get("limit");
841
+ if (limitParam) rows = rows.slice(0, Math.max(1, parseInt(limitParam, 10) || 0));
842
+ return success({ memories: rows.map(memoryDto) });
843
+ }
759
844
  async function handleIdentitiesRequest(request, subPath) {
760
845
  const method = request.method;
761
846
  if (subPath === "" || subPath === "/") {
762
847
  if (method === "GET") return handleList(request);
763
848
  if (method === "POST") return handleCreate(request);
764
849
  }
850
+ if (subPath === "/resolve" && method === "POST") {
851
+ return handleResolve(request);
852
+ }
765
853
  const linksMatch = subPath.match(/^\/([^/]+)\/links$/);
766
854
  if (linksMatch && method === "POST") {
767
855
  return handleAttachLink(linksMatch[1], request);
768
856
  }
857
+ const memoryMatch = subPath.match(/^\/([^/]+)\/memory$/);
858
+ if (memoryMatch) {
859
+ if (method === "GET") return handleListMemory(memoryMatch[1], request);
860
+ if (method === "POST") return handleWriteMemory(memoryMatch[1], request);
861
+ }
769
862
  const singleMatch = subPath.match(/^\/([^/]+)$/);
770
863
  if (singleMatch) {
771
864
  if (method === "GET") return handleGet(singleMatch[1]);
@@ -965,6 +1058,7 @@ async function createTestApp(options = {}) {
965
1058
  state.emailCounter = 0;
966
1059
  state.identities.clear();
967
1060
  state.identityLinks = [];
1061
+ state.identityMemory = [];
968
1062
  },
969
1063
  async close() {
970
1064
  state.db = null;
@@ -974,6 +1068,7 @@ async function createTestApp(options = {}) {
974
1068
  state.emails = [];
975
1069
  state.identities.clear();
976
1070
  state.identityLinks = [];
1071
+ state.identityMemory = [];
977
1072
  uninstallFetchRouter();
978
1073
  await db.close();
979
1074
  }
package/dist/index.mjs CHANGED
@@ -54,6 +54,7 @@ var state = globalSingleton("state", () => ({
54
54
  emailCounter: 0,
55
55
  identities: /* @__PURE__ */ new Map(),
56
56
  identityLinks: [],
57
+ identityMemory: [],
57
58
  allowNetwork: false
58
59
  }));
59
60
  function requireDb() {
@@ -711,16 +712,108 @@ async function handleAttachLink(identityId, request) {
711
712
  state.identityLinks.push(link);
712
713
  return success({ link });
713
714
  }
715
+ function memoryDto(m) {
716
+ return {
717
+ id: m.id,
718
+ source: m.source,
719
+ kind: m.kind,
720
+ content: m.content,
721
+ metadata: m.metadata,
722
+ createdAt: m.createdAt
723
+ };
724
+ }
725
+ async function handleResolve(request) {
726
+ const body = await readBody(request);
727
+ const type = body.type;
728
+ if (type !== "person" && type !== "account") {
729
+ return failure("type must be 'person' or 'account'");
730
+ }
731
+ if (type === "account") {
732
+ return failure(
733
+ "resolveOrCreate is for channel-linked persons; an account is not resolvable by link \u2014 create it explicitly instead.",
734
+ 409
735
+ );
736
+ }
737
+ const link = body.link;
738
+ const kind = link?.kind;
739
+ const externalId = link?.externalId;
740
+ if (typeof kind !== "string" || !LINK_KINDS.has(kind)) {
741
+ return failure(`link.kind must be one of: ${[...LINK_KINDS].join(", ")}`);
742
+ }
743
+ if (typeof externalId !== "string" || !externalId) {
744
+ return failure("link.externalId is required");
745
+ }
746
+ const existingLink = state.identityLinks.find(
747
+ (l) => l.kind === kind && l.externalId === externalId
748
+ );
749
+ if (existingLink) {
750
+ const identity2 = state.identities.get(existingLink.identityId);
751
+ if (identity2) return success({ identity: identity2, created: false });
752
+ }
753
+ const identity = {
754
+ id: crypto3.randomUUID(),
755
+ type,
756
+ parentId: null,
757
+ displayName: body.displayName ?? null,
758
+ profile: body.profile ?? {},
759
+ status: "active",
760
+ mergedIntoId: null,
761
+ externalRef: null,
762
+ createdAt: now(),
763
+ updatedAt: now()
764
+ };
765
+ state.identities.set(identity.id, identity);
766
+ state.identityLinks.push({
767
+ id: crypto3.randomUUID(),
768
+ identityId: identity.id,
769
+ kind,
770
+ externalId,
771
+ verified: true,
772
+ createdAt: now()
773
+ });
774
+ return success({ identity, created: true });
775
+ }
776
+ async function handleWriteMemory(identityId, request) {
777
+ if (!state.identities.get(identityId)) return failure("Identity not found", 404);
778
+ const body = await readBody(request);
779
+ if (typeof body.content !== "string" || !body.content) return failure("content is required");
780
+ const entry = {
781
+ id: crypto3.randomUUID(),
782
+ identityId,
783
+ source: typeof body.source === "string" ? body.source : "app",
784
+ kind: typeof body.kind === "string" ? body.kind : "fact",
785
+ content: body.content,
786
+ metadata: body.metadata ?? {},
787
+ createdAt: now()
788
+ };
789
+ state.identityMemory.push(entry);
790
+ return success({ memory: memoryDto(entry) });
791
+ }
792
+ function handleListMemory(identityId, request) {
793
+ if (!state.identities.get(identityId)) return failure("Identity not found", 404);
794
+ let rows = state.identityMemory.filter((m) => m.identityId === identityId).reverse();
795
+ const limitParam = new URL(request.url).searchParams.get("limit");
796
+ if (limitParam) rows = rows.slice(0, Math.max(1, parseInt(limitParam, 10) || 0));
797
+ return success({ memories: rows.map(memoryDto) });
798
+ }
714
799
  async function handleIdentitiesRequest(request, subPath) {
715
800
  const method = request.method;
716
801
  if (subPath === "" || subPath === "/") {
717
802
  if (method === "GET") return handleList(request);
718
803
  if (method === "POST") return handleCreate(request);
719
804
  }
805
+ if (subPath === "/resolve" && method === "POST") {
806
+ return handleResolve(request);
807
+ }
720
808
  const linksMatch = subPath.match(/^\/([^/]+)\/links$/);
721
809
  if (linksMatch && method === "POST") {
722
810
  return handleAttachLink(linksMatch[1], request);
723
811
  }
812
+ const memoryMatch = subPath.match(/^\/([^/]+)\/memory$/);
813
+ if (memoryMatch) {
814
+ if (method === "GET") return handleListMemory(memoryMatch[1], request);
815
+ if (method === "POST") return handleWriteMemory(memoryMatch[1], request);
816
+ }
724
817
  const singleMatch = subPath.match(/^\/([^/]+)$/);
725
818
  if (singleMatch) {
726
819
  if (method === "GET") return handleGet(singleMatch[1]);
@@ -920,6 +1013,7 @@ async function createTestApp(options = {}) {
920
1013
  state.emailCounter = 0;
921
1014
  state.identities.clear();
922
1015
  state.identityLinks = [];
1016
+ state.identityMemory = [];
923
1017
  },
924
1018
  async close() {
925
1019
  state.db = null;
@@ -929,6 +1023,7 @@ async function createTestApp(options = {}) {
929
1023
  state.emails = [];
930
1024
  state.identities.clear();
931
1025
  state.identityLinks = [];
1026
+ state.identityMemory = [];
932
1027
  uninstallFetchRouter();
933
1028
  await db.close();
934
1029
  }
@@ -46,6 +46,7 @@ var state = globalSingleton("state", () => ({
46
46
  emailCounter: 0,
47
47
  identities: /* @__PURE__ */ new Map(),
48
48
  identityLinks: [],
49
+ identityMemory: [],
49
50
  allowNetwork: false
50
51
  }));
51
52
 
@@ -19,6 +19,7 @@ var state = globalSingleton("state", () => ({
19
19
  emailCounter: 0,
20
20
  identities: /* @__PURE__ */ new Map(),
21
21
  identityLinks: [],
22
+ identityMemory: [],
22
23
  allowNetwork: false
23
24
  }));
24
25
 
package/dist/setup.js CHANGED
@@ -40,6 +40,7 @@ var state = globalSingleton("state", () => ({
40
40
  emailCounter: 0,
41
41
  identities: /* @__PURE__ */ new Map(),
42
42
  identityLinks: [],
43
+ identityMemory: [],
43
44
  allowNetwork: false
44
45
  }));
45
46
  function requireDb() {
@@ -687,16 +688,108 @@ async function handleAttachLink(identityId, request) {
687
688
  state.identityLinks.push(link);
688
689
  return success({ link });
689
690
  }
691
+ function memoryDto(m) {
692
+ return {
693
+ id: m.id,
694
+ source: m.source,
695
+ kind: m.kind,
696
+ content: m.content,
697
+ metadata: m.metadata,
698
+ createdAt: m.createdAt
699
+ };
700
+ }
701
+ async function handleResolve(request) {
702
+ const body = await readBody(request);
703
+ const type = body.type;
704
+ if (type !== "person" && type !== "account") {
705
+ return failure("type must be 'person' or 'account'");
706
+ }
707
+ if (type === "account") {
708
+ return failure(
709
+ "resolveOrCreate is for channel-linked persons; an account is not resolvable by link \u2014 create it explicitly instead.",
710
+ 409
711
+ );
712
+ }
713
+ const link = body.link;
714
+ const kind = link?.kind;
715
+ const externalId = link?.externalId;
716
+ if (typeof kind !== "string" || !LINK_KINDS.has(kind)) {
717
+ return failure(`link.kind must be one of: ${[...LINK_KINDS].join(", ")}`);
718
+ }
719
+ if (typeof externalId !== "string" || !externalId) {
720
+ return failure("link.externalId is required");
721
+ }
722
+ const existingLink = state.identityLinks.find(
723
+ (l) => l.kind === kind && l.externalId === externalId
724
+ );
725
+ if (existingLink) {
726
+ const identity2 = state.identities.get(existingLink.identityId);
727
+ if (identity2) return success({ identity: identity2, created: false });
728
+ }
729
+ const identity = {
730
+ id: import_node_crypto3.default.randomUUID(),
731
+ type,
732
+ parentId: null,
733
+ displayName: body.displayName ?? null,
734
+ profile: body.profile ?? {},
735
+ status: "active",
736
+ mergedIntoId: null,
737
+ externalRef: null,
738
+ createdAt: now(),
739
+ updatedAt: now()
740
+ };
741
+ state.identities.set(identity.id, identity);
742
+ state.identityLinks.push({
743
+ id: import_node_crypto3.default.randomUUID(),
744
+ identityId: identity.id,
745
+ kind,
746
+ externalId,
747
+ verified: true,
748
+ createdAt: now()
749
+ });
750
+ return success({ identity, created: true });
751
+ }
752
+ async function handleWriteMemory(identityId, request) {
753
+ if (!state.identities.get(identityId)) return failure("Identity not found", 404);
754
+ const body = await readBody(request);
755
+ if (typeof body.content !== "string" || !body.content) return failure("content is required");
756
+ const entry = {
757
+ id: import_node_crypto3.default.randomUUID(),
758
+ identityId,
759
+ source: typeof body.source === "string" ? body.source : "app",
760
+ kind: typeof body.kind === "string" ? body.kind : "fact",
761
+ content: body.content,
762
+ metadata: body.metadata ?? {},
763
+ createdAt: now()
764
+ };
765
+ state.identityMemory.push(entry);
766
+ return success({ memory: memoryDto(entry) });
767
+ }
768
+ function handleListMemory(identityId, request) {
769
+ if (!state.identities.get(identityId)) return failure("Identity not found", 404);
770
+ let rows = state.identityMemory.filter((m) => m.identityId === identityId).reverse();
771
+ const limitParam = new URL(request.url).searchParams.get("limit");
772
+ if (limitParam) rows = rows.slice(0, Math.max(1, parseInt(limitParam, 10) || 0));
773
+ return success({ memories: rows.map(memoryDto) });
774
+ }
690
775
  async function handleIdentitiesRequest(request, subPath) {
691
776
  const method = request.method;
692
777
  if (subPath === "" || subPath === "/") {
693
778
  if (method === "GET") return handleList(request);
694
779
  if (method === "POST") return handleCreate(request);
695
780
  }
781
+ if (subPath === "/resolve" && method === "POST") {
782
+ return handleResolve(request);
783
+ }
696
784
  const linksMatch = subPath.match(/^\/([^/]+)\/links$/);
697
785
  if (linksMatch && method === "POST") {
698
786
  return handleAttachLink(linksMatch[1], request);
699
787
  }
788
+ const memoryMatch = subPath.match(/^\/([^/]+)\/memory$/);
789
+ if (memoryMatch) {
790
+ if (method === "GET") return handleListMemory(memoryMatch[1], request);
791
+ if (method === "POST") return handleWriteMemory(memoryMatch[1], request);
792
+ }
700
793
  const singleMatch = subPath.match(/^\/([^/]+)$/);
701
794
  if (singleMatch) {
702
795
  if (method === "GET") return handleGet(singleMatch[1]);
package/dist/setup.mjs CHANGED
@@ -16,6 +16,7 @@ var state = globalSingleton("state", () => ({
16
16
  emailCounter: 0,
17
17
  identities: /* @__PURE__ */ new Map(),
18
18
  identityLinks: [],
19
+ identityMemory: [],
19
20
  allowNetwork: false
20
21
  }));
21
22
  function requireDb() {
@@ -663,16 +664,108 @@ async function handleAttachLink(identityId, request) {
663
664
  state.identityLinks.push(link);
664
665
  return success({ link });
665
666
  }
667
+ function memoryDto(m) {
668
+ return {
669
+ id: m.id,
670
+ source: m.source,
671
+ kind: m.kind,
672
+ content: m.content,
673
+ metadata: m.metadata,
674
+ createdAt: m.createdAt
675
+ };
676
+ }
677
+ async function handleResolve(request) {
678
+ const body = await readBody(request);
679
+ const type = body.type;
680
+ if (type !== "person" && type !== "account") {
681
+ return failure("type must be 'person' or 'account'");
682
+ }
683
+ if (type === "account") {
684
+ return failure(
685
+ "resolveOrCreate is for channel-linked persons; an account is not resolvable by link \u2014 create it explicitly instead.",
686
+ 409
687
+ );
688
+ }
689
+ const link = body.link;
690
+ const kind = link?.kind;
691
+ const externalId = link?.externalId;
692
+ if (typeof kind !== "string" || !LINK_KINDS.has(kind)) {
693
+ return failure(`link.kind must be one of: ${[...LINK_KINDS].join(", ")}`);
694
+ }
695
+ if (typeof externalId !== "string" || !externalId) {
696
+ return failure("link.externalId is required");
697
+ }
698
+ const existingLink = state.identityLinks.find(
699
+ (l) => l.kind === kind && l.externalId === externalId
700
+ );
701
+ if (existingLink) {
702
+ const identity2 = state.identities.get(existingLink.identityId);
703
+ if (identity2) return success({ identity: identity2, created: false });
704
+ }
705
+ const identity = {
706
+ id: crypto3.randomUUID(),
707
+ type,
708
+ parentId: null,
709
+ displayName: body.displayName ?? null,
710
+ profile: body.profile ?? {},
711
+ status: "active",
712
+ mergedIntoId: null,
713
+ externalRef: null,
714
+ createdAt: now(),
715
+ updatedAt: now()
716
+ };
717
+ state.identities.set(identity.id, identity);
718
+ state.identityLinks.push({
719
+ id: crypto3.randomUUID(),
720
+ identityId: identity.id,
721
+ kind,
722
+ externalId,
723
+ verified: true,
724
+ createdAt: now()
725
+ });
726
+ return success({ identity, created: true });
727
+ }
728
+ async function handleWriteMemory(identityId, request) {
729
+ if (!state.identities.get(identityId)) return failure("Identity not found", 404);
730
+ const body = await readBody(request);
731
+ if (typeof body.content !== "string" || !body.content) return failure("content is required");
732
+ const entry = {
733
+ id: crypto3.randomUUID(),
734
+ identityId,
735
+ source: typeof body.source === "string" ? body.source : "app",
736
+ kind: typeof body.kind === "string" ? body.kind : "fact",
737
+ content: body.content,
738
+ metadata: body.metadata ?? {},
739
+ createdAt: now()
740
+ };
741
+ state.identityMemory.push(entry);
742
+ return success({ memory: memoryDto(entry) });
743
+ }
744
+ function handleListMemory(identityId, request) {
745
+ if (!state.identities.get(identityId)) return failure("Identity not found", 404);
746
+ let rows = state.identityMemory.filter((m) => m.identityId === identityId).reverse();
747
+ const limitParam = new URL(request.url).searchParams.get("limit");
748
+ if (limitParam) rows = rows.slice(0, Math.max(1, parseInt(limitParam, 10) || 0));
749
+ return success({ memories: rows.map(memoryDto) });
750
+ }
666
751
  async function handleIdentitiesRequest(request, subPath) {
667
752
  const method = request.method;
668
753
  if (subPath === "" || subPath === "/") {
669
754
  if (method === "GET") return handleList(request);
670
755
  if (method === "POST") return handleCreate(request);
671
756
  }
757
+ if (subPath === "/resolve" && method === "POST") {
758
+ return handleResolve(request);
759
+ }
672
760
  const linksMatch = subPath.match(/^\/([^/]+)\/links$/);
673
761
  if (linksMatch && method === "POST") {
674
762
  return handleAttachLink(linksMatch[1], request);
675
763
  }
764
+ const memoryMatch = subPath.match(/^\/([^/]+)\/memory$/);
765
+ if (memoryMatch) {
766
+ if (method === "GET") return handleListMemory(memoryMatch[1], request);
767
+ if (method === "POST") return handleWriteMemory(memoryMatch[1], request);
768
+ }
676
769
  const singleMatch = subPath.match(/^\/([^/]+)$/);
677
770
  if (singleMatch) {
678
771
  if (method === "GET") return handleGet(singleMatch[1]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stardeck-customer-apps/testing",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Vitest test harness for Stardeck customer apps — in-process Postgres (PGlite) plus a control-plane simulator so the real Stardeck SDKs run unmodified in tests",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",